manim 0.18.0.post0__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 (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  """Mobjects that represent coordinate systems."""
2
2
 
3
-
4
3
  from __future__ import annotations
5
4
 
6
5
  __all__ = [
@@ -14,7 +13,8 @@ __all__ = [
14
13
 
15
14
  import fractions as fr
16
15
  import numbers
17
- 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
18
18
 
19
19
  import numpy as np
20
20
  from typing_extensions import Self
@@ -27,6 +27,7 @@ from manim.mobject.geometry.polygram import Polygon, Rectangle, RegularPolygon
27
27
  from manim.mobject.graphing.functions import ImplicitFunction, ParametricFunction
28
28
  from manim.mobject.graphing.number_line import NumberLine
29
29
  from manim.mobject.graphing.scale import LinearBase
30
+ from manim.mobject.mobject import Mobject
30
31
  from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
31
32
  from manim.mobject.opengl.opengl_surface import OpenGLSurface
32
33
  from manim.mobject.text.tex_mobject import MathTex
@@ -47,6 +48,7 @@ from manim.utils.color import (
47
48
  ManimColor,
48
49
  ParsableManimColor,
49
50
  color_gradient,
51
+ interpolate_color,
50
52
  invert_color,
51
53
  )
52
54
  from manim.utils.config_ops import merge_dicts_recursively, update_dict_recursively
@@ -55,7 +57,14 @@ from manim.utils.space_ops import angle_of_vector
55
57
 
56
58
  if TYPE_CHECKING:
57
59
  from manim.mobject.mobject import Mobject
58
- from manim.typing import ManimFloat, Point2D, Point3D, Vector3
60
+ from manim.typing import (
61
+ ManimFloat,
62
+ Point2D,
63
+ Point2DLike,
64
+ Point3D,
65
+ Point3DLike,
66
+ Vector3D,
67
+ )
59
68
 
60
69
  LineType = TypeVar("LineType", bound=Line)
61
70
 
@@ -96,10 +105,10 @@ class CoordinateSystem:
96
105
  )
97
106
 
98
107
  # Extra lines and labels for point (1,1)
99
- graphs += grid.get_horizontal_line(grid.c2p(1, 1, 0), color=BLUE)
100
- graphs += grid.get_vertical_line(grid.c2p(1, 1, 0), color=BLUE)
101
- graphs += Dot(point=grid.c2p(1, 1, 0), color=YELLOW)
102
- 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))
103
112
  title = Title(
104
113
  # spaces between braces to prevent SyntaxError
105
114
  r"Graphs of $y=x^{ {1}\over{n} }$ and $y=x^n (n=1,2,3,...,20)$",
@@ -145,10 +154,10 @@ class CoordinateSystem:
145
154
  self.y_length = y_length
146
155
  self.num_sampled_graph_points_per_tick = 10
147
156
 
148
- def coords_to_point(self, *coords: Sequence[ManimFloat]):
157
+ def coords_to_point(self, *coords: ManimFloat):
149
158
  raise NotImplementedError()
150
159
 
151
- def point_to_coords(self, point: Point3D):
160
+ def point_to_coords(self, point: Point3DLike):
152
161
  raise NotImplementedError()
153
162
 
154
163
  def polar_to_point(self, radius: float, azimuth: float) -> Point2D:
@@ -182,7 +191,7 @@ class CoordinateSystem:
182
191
  """
183
192
  return self.coords_to_point(radius * np.cos(azimuth), radius * np.sin(azimuth))
184
193
 
185
- def point_to_polar(self, point: np.ndarray) -> Point2D:
194
+ def point_to_polar(self, point: Point2DLike) -> Point2D:
186
195
  r"""Gets polar coordinates from a point.
187
196
 
188
197
  Parameters
@@ -204,7 +213,7 @@ class CoordinateSystem:
204
213
  """Abbreviation for :meth:`coords_to_point`"""
205
214
  return self.coords_to_point(*coords)
206
215
 
207
- def p2c(self, point: Point3D):
216
+ def p2c(self, point: Point3DLike):
208
217
  """Abbreviation for :meth:`point_to_coords`"""
209
218
  return self.point_to_coords(point)
210
219
 
@@ -305,7 +314,7 @@ class CoordinateSystem:
305
314
  label
306
315
  The label. Defaults to :class:`~.MathTex` for ``str`` and ``float`` inputs.
307
316
  edge
308
- The edge of the x-axis to which the label will be added, by default ``UR``.
317
+ The edge of the y-axis to which the label will be added, by default ``UR``.
309
318
  direction
310
319
  Allows for further positioning of the label from an edge, by default ``UR``
311
320
  buff
@@ -332,7 +341,6 @@ class CoordinateSystem:
332
341
  )
333
342
  self.add(ax, y_label)
334
343
  """
335
-
336
344
  return self._get_axis_label(
337
345
  label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
338
346
  )
@@ -365,7 +373,6 @@ class CoordinateSystem:
365
373
  :class:`~.Mobject`
366
374
  The positioned label along the given axis.
367
375
  """
368
-
369
376
  label = self.x_axis._create_label_tex(label)
370
377
  label.next_to(axis.get_edge_center(edge), direction=direction, buff=buff)
371
378
  label.shift_onto_screen(buff=MED_SMALL_BUFF)
@@ -394,7 +401,9 @@ class CoordinateSystem:
394
401
  ax = ThreeDAxes()
395
402
  x_labels = range(-4, 5)
396
403
  z_labels = range(-4, 4, 2)
397
- 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
398
407
  ax.add_coordinates(x_labels) # only x labels
399
408
 
400
409
  You can also specifically control the position and value of the labels using a dict.
@@ -405,11 +414,18 @@ class CoordinateSystem:
405
414
  x_pos = [x for x in range(1, 8)]
406
415
 
407
416
  # strings are automatically converted into a Tex mobject.
408
- 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
+ ]
409
426
  x_dict = dict(zip(x_pos, x_vals))
410
427
  ax.add_coordinates(x_dict)
411
428
  """
412
-
413
429
  self.coordinate_labels = VGroup()
414
430
  # if nothing is passed to axes_numbers, produce axes with default labelling
415
431
  if not axes_numbers:
@@ -441,8 +457,7 @@ class CoordinateSystem:
441
457
  line_config: dict | None = ...,
442
458
  color: ParsableManimColor | None = ...,
443
459
  stroke_width: float = ...,
444
- ) -> DashedLine:
445
- ...
460
+ ) -> DashedLine: ...
446
461
 
447
462
  @overload
448
463
  def get_line_from_axis_to_point(
@@ -453,8 +468,7 @@ class CoordinateSystem:
453
468
  line_config: dict | None = ...,
454
469
  color: ParsableManimColor | None = ...,
455
470
  stroke_width: float = ...,
456
- ) -> LineType:
457
- ...
471
+ ) -> LineType: ...
458
472
 
459
473
  def get_line_from_axis_to_point( # type: ignore[no-untyped-def]
460
474
  self,
@@ -492,7 +506,6 @@ class CoordinateSystem:
492
506
  :meth:`~.CoordinateSystem.get_vertical_line`
493
507
  :meth:`~.CoordinateSystem.get_horizontal_line`
494
508
  """
495
-
496
509
  line_config = line_config if line_config is not None else {}
497
510
 
498
511
  if color is None:
@@ -562,14 +575,13 @@ class CoordinateSystem:
562
575
  class GetHorizontalLineExample(Scene):
563
576
  def construct(self):
564
577
  ax = Axes().add_coordinates()
565
- point = ax.c2p(-4, 1.5)
578
+ point = ax @ (-4, 1.5)
566
579
 
567
580
  dot = Dot(point)
568
581
  line = ax.get_horizontal_line(point, line_func=Line)
569
582
 
570
583
  self.add(ax, line, dot)
571
584
  """
572
-
573
585
  return self.get_line_from_axis_to_point(1, point, **kwargs)
574
586
 
575
587
  def get_lines_to_point(self, point: Sequence[float], **kwargs) -> VGroup:
@@ -606,7 +618,6 @@ class CoordinateSystem:
606
618
  lines_2 = ax.get_lines_to_point(circ.get_corner(DL), color=BLUE_B)
607
619
  self.add(ax, lines_1, lines_2, circ)
608
620
  """
609
-
610
621
  return VGroup(
611
622
  self.get_horizontal_line(point, **kwargs),
612
623
  self.get_vertical_line(point, **kwargs),
@@ -619,6 +630,8 @@ class CoordinateSystem:
619
630
  function: Callable[[float], float],
620
631
  x_range: Sequence[float] | None = None,
621
632
  use_vectorized: bool = False,
633
+ colorscale: Union[Iterable[Color], Iterable[Color, float]] | None = None,
634
+ colorscale_axis: int = 1,
622
635
  **kwargs: Any,
623
636
  ) -> ParametricFunction:
624
637
  """Generates a curve based on a function.
@@ -632,6 +645,12 @@ class CoordinateSystem:
632
645
  use_vectorized
633
646
  Whether to pass in the generated t value array to the function. Only use this if your function supports it.
634
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).
635
654
  kwargs
636
655
  Additional parameters to be passed to :class:`~.ParametricFunction`.
637
656
 
@@ -691,7 +710,6 @@ class CoordinateSystem:
691
710
 
692
711
  self.add(axes, curves)
693
712
  """
694
-
695
713
  t_range = np.array(self.x_range, dtype=float)
696
714
  if x_range is not None:
697
715
  t_range[: len(x_range)] = x_range
@@ -710,7 +728,57 @@ class CoordinateSystem:
710
728
  use_vectorized=use_vectorized,
711
729
  **kwargs,
712
730
  )
731
+
713
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
+
714
782
  return graph
715
783
 
716
784
  def plot_implicit_curve(
@@ -855,9 +923,11 @@ class CoordinateSystem:
855
923
  function: Callable[[float], float],
856
924
  u_range: Sequence[float] | None = None,
857
925
  v_range: Sequence[float] | None = None,
858
- colorscale: Sequence[ParsableManimColor]
859
- | Sequence[tuple[ParsableManimColor, float]]
860
- | None = None,
926
+ colorscale: (
927
+ Sequence[ParsableManimColor]
928
+ | Sequence[tuple[ParsableManimColor, float]]
929
+ | None
930
+ ) = None,
861
931
  colorscale_axis: int = 2,
862
932
  **kwargs: Any,
863
933
  ) -> Surface | OpenGLSurface:
@@ -940,7 +1010,7 @@ class CoordinateSystem:
940
1010
  self,
941
1011
  x: float,
942
1012
  graph: ParametricFunction | VMobject,
943
- ) -> np.ndarray:
1013
+ ) -> Point3D:
944
1014
  """Returns the coordinates of the point on a ``graph`` corresponding to an ``x`` value.
945
1015
 
946
1016
  Parameters
@@ -976,7 +1046,6 @@ class CoordinateSystem:
976
1046
 
977
1047
  self.add(ax, curve, sq)
978
1048
  """
979
-
980
1049
  if hasattr(graph, "underlying_function"):
981
1050
  return graph.function(x)
982
1051
  else:
@@ -1032,7 +1101,7 @@ class CoordinateSystem:
1032
1101
  dot: bool = False,
1033
1102
  dot_config: dict[str, Any] | None = None,
1034
1103
  ) -> Mobject:
1035
- """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.
1036
1105
 
1037
1106
  Parameters
1038
1107
  ----------
@@ -1069,7 +1138,7 @@ class CoordinateSystem:
1069
1138
  sin = ax.plot(lambda x: np.sin(x), color=PURPLE_B)
1070
1139
  label = ax.get_graph_label(
1071
1140
  graph=sin,
1072
- label= MathTex(r"\\frac{\\pi}{2}"),
1141
+ label= MathTex(r"\frac{\pi}{2}"),
1073
1142
  x_val=PI / 2,
1074
1143
  dot=True,
1075
1144
  direction=UR,
@@ -1077,7 +1146,6 @@ class CoordinateSystem:
1077
1146
 
1078
1147
  self.add(ax, sin, label)
1079
1148
  """
1080
-
1081
1149
  if dot_config is None:
1082
1150
  dot_config = {}
1083
1151
  if color is None:
@@ -1197,7 +1265,6 @@ class CoordinateSystem:
1197
1265
  ax, bounding_line, quadratic, rects_right, rects_left, bounded_rects
1198
1266
  )
1199
1267
  """
1200
-
1201
1268
  # setting up x_range, overwrite user's third input
1202
1269
  if x_range is None:
1203
1270
  if bounded_graph is None:
@@ -1387,7 +1454,6 @@ class CoordinateSystem:
1387
1454
  ax.angle_of_tangent(x=3, graph=curve)
1388
1455
  # 1.4056476493802699
1389
1456
  """
1390
-
1391
1457
  p0 = np.array([*self.input_to_graph_coords(x, graph)])
1392
1458
  p1 = np.array([*self.input_to_graph_coords(x + dx, graph)])
1393
1459
  return angle_of_vector(p1 - p0)
@@ -1419,7 +1485,6 @@ class CoordinateSystem:
1419
1485
  ax.slope_of_tangent(x=-2, graph=curve)
1420
1486
  # -3.5000000259052038
1421
1487
  """
1422
-
1423
1488
  return np.tan(self.angle_of_tangent(x, graph, **kwargs))
1424
1489
 
1425
1490
  def plot_derivative_graph(
@@ -1695,7 +1760,6 @@ class CoordinateSystem:
1695
1760
 
1696
1761
  self.add(ax, curve, lines)
1697
1762
  """
1698
-
1699
1763
  x_range = x_range if x_range is not None else self.x_range
1700
1764
 
1701
1765
  return VGroup(
@@ -1757,7 +1821,6 @@ class CoordinateSystem:
1757
1821
  t_label = axes.get_T_label(x_val=4, graph=func, label=Tex("x-value"))
1758
1822
  self.add(axes, func, t_label)
1759
1823
  """
1760
-
1761
1824
  T_label_group = VGroup()
1762
1825
  triangle = RegularPolygon(n=3, start_angle=np.pi / 2, stroke_width=0).set_fill(
1763
1826
  color=triangle_color,
@@ -1780,6 +1843,14 @@ class CoordinateSystem:
1780
1843
 
1781
1844
  return T_label_group
1782
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
+
1783
1854
 
1784
1855
  class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1785
1856
  """Creates a set of axes.
@@ -1941,7 +2012,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1941
2012
  )
1942
2013
  )
1943
2014
  """
1944
-
1945
2015
  for default_config, passed_config in zip(default_configs, passed_configs):
1946
2016
  if passed_config is not None:
1947
2017
  update_dict_recursively(default_config, passed_config)
@@ -1980,6 +2050,7 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
1980
2050
  self, *coords: float | Sequence[float] | Sequence[Sequence[float]] | np.ndarray
1981
2051
  ) -> np.ndarray:
1982
2052
  """Accepts coordinates from the axes and returns a point with respect to the scene.
2053
+ Equivalent to `ax @ (coord1)`
1983
2054
 
1984
2055
  Parameters
1985
2056
  ----------
@@ -2008,6 +2079,8 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
2008
2079
  >>> ax = Axes()
2009
2080
  >>> np.around(ax.coords_to_point(1, 0, 0), 2)
2010
2081
  array([0.86, 0. , 0. ])
2082
+ >>> np.around(ax @ (1, 0, 0), 2)
2083
+ array([0.86, 0. , 0. ])
2011
2084
  >>> np.around(ax.coords_to_point([[0, 1], [1, 1], [1, 0]]), 2)
2012
2085
  array([[0. , 0.75, 0. ],
2013
2086
  [0.86, 0.75, 0. ],
@@ -2183,7 +2256,6 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
2183
2256
  )
2184
2257
  self.add(ax, labels)
2185
2258
  """
2186
-
2187
2259
  self.axis_labels = VGroup(
2188
2260
  self.get_x_axis_label(x_label),
2189
2261
  self.get_y_axis_label(y_label),
@@ -2342,7 +2414,7 @@ class ThreeDAxes(Axes):
2342
2414
  y_length: float | None = config.frame_height + 2.5,
2343
2415
  z_length: float | None = config.frame_height - 1.5,
2344
2416
  z_axis_config: dict[str, Any] | None = None,
2345
- z_normal: Vector3 = DOWN,
2417
+ z_normal: Vector3D = DOWN,
2346
2418
  num_axis_pieces: int = 20,
2347
2419
  light_source: Sequence[float] = 9 * DOWN + 7 * LEFT + 10 * OUT,
2348
2420
  # opengl stuff (?)
@@ -2433,7 +2505,7 @@ class ThreeDAxes(Axes):
2433
2505
  direction: Sequence[float] = UR,
2434
2506
  buff: float = SMALL_BUFF,
2435
2507
  rotation: float = PI / 2,
2436
- rotation_axis: Vector3 = OUT,
2508
+ rotation_axis: Vector3D = OUT,
2437
2509
  **kwargs,
2438
2510
  ) -> Mobject:
2439
2511
  """Generate a y-axis label.
@@ -2470,7 +2542,6 @@ class ThreeDAxes(Axes):
2470
2542
  self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
2471
2543
  self.add(ax, lab)
2472
2544
  """
2473
-
2474
2545
  positioned_label = self._get_axis_label(
2475
2546
  label, self.get_y_axis(), edge, direction, buff=buff, **kwargs
2476
2547
  )
@@ -2480,11 +2551,11 @@ class ThreeDAxes(Axes):
2480
2551
  def get_z_axis_label(
2481
2552
  self,
2482
2553
  label: float | str | Mobject,
2483
- edge: Vector3 = OUT,
2484
- direction: Vector3 = RIGHT,
2554
+ edge: Vector3D = OUT,
2555
+ direction: Vector3D = RIGHT,
2485
2556
  buff: float = SMALL_BUFF,
2486
2557
  rotation: float = PI / 2,
2487
- rotation_axis: Vector3 = RIGHT,
2558
+ rotation_axis: Vector3D = RIGHT,
2488
2559
  **kwargs: Any,
2489
2560
  ) -> Mobject:
2490
2561
  """Generate a z-axis label.
@@ -2521,7 +2592,6 @@ class ThreeDAxes(Axes):
2521
2592
  self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
2522
2593
  self.add(ax, lab)
2523
2594
  """
2524
-
2525
2595
  positioned_label = self._get_axis_label(
2526
2596
  label, self.get_z_axis(), edge, direction, buff=buff, **kwargs
2527
2597
  )
@@ -2571,11 +2641,10 @@ class ThreeDAxes(Axes):
2571
2641
  self.set_camera_orientation(phi=2*PI/5, theta=PI/5)
2572
2642
  axes = ThreeDAxes()
2573
2643
  labels = axes.get_axis_labels(
2574
- Tex("x-axis").scale(0.7), Text("y-axis").scale(0.45), Text("z-axis").scale(0.45)
2644
+ Text("x-axis").scale(0.7), Text("y-axis").scale(0.45), Text("z-axis").scale(0.45)
2575
2645
  )
2576
2646
  self.add(axes, labels)
2577
2647
  """
2578
-
2579
2648
  self.axis_labels = VGroup(
2580
2649
  self.get_x_axis_label(x_label),
2581
2650
  self.get_y_axis_label(y_label),
@@ -2653,14 +2722,12 @@ class NumberPlane(Axes):
2653
2722
 
2654
2723
  def __init__(
2655
2724
  self,
2656
- x_range: Sequence[float]
2657
- | None = (
2725
+ x_range: Sequence[float] | None = (
2658
2726
  -config["frame_x_radius"],
2659
2727
  config["frame_x_radius"],
2660
2728
  1,
2661
2729
  ),
2662
- y_range: Sequence[float]
2663
- | None = (
2730
+ y_range: Sequence[float] | None = (
2664
2731
  -config["frame_y_radius"],
2665
2732
  config["frame_y_radius"],
2666
2733
  1,
@@ -2801,7 +2868,6 @@ class NumberPlane(Axes):
2801
2868
  The first (i.e the non-faded lines parallel to `axis_parallel_to`) and second
2802
2869
  (i.e the faded lines parallel to `axis_parallel_to`) sets of lines, respectively.
2803
2870
  """
2804
-
2805
2871
  line = Line(axis_parallel_to.get_start(), axis_parallel_to.get_end())
2806
2872
  if ratio_faded_lines == 0: # don't show faded lines
2807
2873
  ratio_faded_lines = 1 # i.e. set ratio to 1
@@ -3169,7 +3235,7 @@ class PolarPlane(Axes):
3169
3235
  elif self.azimuth_units == "degrees":
3170
3236
  a_tex = [
3171
3237
  MathTex(
3172
- f'{360 * i["label"]:g}' + r"^{\circ}",
3238
+ f"{360 * i['label']:g}" + r"^{\circ}",
3173
3239
  font_size=self.azimuth_label_font_size,
3174
3240
  ).next_to(
3175
3241
  i["point"],
@@ -3182,7 +3248,7 @@ class PolarPlane(Axes):
3182
3248
  elif self.azimuth_units == "gradians":
3183
3249
  a_tex = [
3184
3250
  MathTex(
3185
- f'{400 * i["label"]:g}' + r"^{g}",
3251
+ f"{400 * i['label']:g}" + r"^{g}",
3186
3252
  font_size=self.azimuth_label_font_size,
3187
3253
  ).next_to(
3188
3254
  i["point"],
@@ -3195,7 +3261,7 @@ class PolarPlane(Axes):
3195
3261
  elif self.azimuth_units is None:
3196
3262
  a_tex = [
3197
3263
  MathTex(
3198
- f'{i["label"]:g}',
3264
+ f"{i['label']:g}",
3199
3265
  font_size=self.azimuth_label_font_size,
3200
3266
  ).next_to(
3201
3267
  i["point"],
@@ -3313,7 +3379,6 @@ class ComplexPlane(NumberPlane):
3313
3379
  np.ndarray
3314
3380
  The point on the plane.
3315
3381
  """
3316
-
3317
3382
  number = complex(number)
3318
3383
  return self.coords_to_point(number.real, number.imag)
3319
3384
 
@@ -3321,7 +3386,7 @@ class ComplexPlane(NumberPlane):
3321
3386
  """Abbreviation for :meth:`number_to_point`."""
3322
3387
  return self.number_to_point(number)
3323
3388
 
3324
- def point_to_number(self, point: Point3D) -> complex:
3389
+ def point_to_number(self, point: Point3DLike) -> complex:
3325
3390
  """Accepts a point and returns a complex number equivalent to that point on the plane.
3326
3391
 
3327
3392
  Parameters
@@ -3334,11 +3399,10 @@ class ComplexPlane(NumberPlane):
3334
3399
  complex
3335
3400
  A complex number consisting of real and imaginary components.
3336
3401
  """
3337
-
3338
3402
  x, y = self.point_to_coords(point)
3339
3403
  return complex(x, y)
3340
3404
 
3341
- def p2n(self, point: Point3D) -> complex:
3405
+ def p2n(self, point: Point3DLike) -> complex:
3342
3406
  """Abbreviation for :meth:`point_to_number`."""
3343
3407
  return self.point_to_number(point)
3344
3408
 
@@ -3372,7 +3436,6 @@ class ComplexPlane(NumberPlane):
3372
3436
  :class:`~.VGroup`
3373
3437
  A :class:`~.VGroup` containing the positioned label mobjects.
3374
3438
  """
3375
-
3376
3439
  # TODO: Make this work the same as coord_sys.add_coordinates()
3377
3440
  if len(numbers) == 0:
3378
3441
  numbers = self._get_default_coordinate_values()
@@ -3403,6 +3466,5 @@ class ComplexPlane(NumberPlane):
3403
3466
  kwargs
3404
3467
  Additional arguments to be passed to :meth:`~.NumberLine.get_number_mobject`, i.e. :class:`~.DecimalNumber`.
3405
3468
  """
3406
-
3407
3469
  self.add(self.get_coordinate_labels(*numbers, **kwargs))
3408
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 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
@@ -14,6 +15,12 @@ from manim import config
14
15
  from manim.mobject.graphing.scale import LinearBase, _ScaleBase
15
16
  from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
16
17
  from manim.mobject.types.vectorized_mobject import VMobject
18
+
19
+ if TYPE_CHECKING:
20
+ from typing_extensions import Self
21
+
22
+ from manim.typing import Point3D, Point3DLike
23
+
17
24
  from manim.utils.color import YELLOW
18
25
 
19
26
 
@@ -23,9 +30,9 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
23
30
  Parameters
24
31
  ----------
25
32
  function
26
- The function to be plotted in the form of ``(lambda x: x**2)``
33
+ The function to be plotted in the form of ``(lambda t: (x(t), y(t), z(t)))``
27
34
  t_range
28
- Determines the length that the function spans. By default ``[0, 1]``
35
+ Determines the length that the function spans in the form of (t_min, t_max, step=0.01). By default ``[0, 1]``
29
36
  scaling
30
37
  Scaling class applied to the points of the function. Default of :class:`~.LinearBase`.
31
38
  use_smoothing
@@ -49,10 +56,10 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
49
56
 
50
57
  class PlotParametricFunction(Scene):
51
58
  def func(self, t):
52
- return np.array((np.sin(2 * t), np.sin(3 * t), 0))
59
+ return (np.sin(2 * t), np.sin(3 * t), 0)
53
60
 
54
61
  def construct(self):
55
- func = ParametricFunction(self.func, t_range = np.array([0, TAU]), fill_opacity=0).set_color(RED)
62
+ func = ParametricFunction(self.func, t_range = (0, TAU), fill_opacity=0).set_color(RED)
56
63
  self.add(func.scale(3))
57
64
 
58
65
  .. manim:: ThreeDParametricSpring
@@ -61,11 +68,11 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
61
68
  class ThreeDParametricSpring(ThreeDScene):
62
69
  def construct(self):
63
70
  curve1 = ParametricFunction(
64
- lambda u: np.array([
71
+ lambda u: (
65
72
  1.2 * np.cos(u),
66
73
  1.2 * np.sin(u),
67
74
  u * 0.05
68
- ]), color=RED, t_range = np.array([-3*TAU, 5*TAU, 0.01])
75
+ ), color=RED, t_range = (-3*TAU, 5*TAU, 0.01)
69
76
  ).set_shade_in_3d(True)
70
77
  axes = ThreeDAxes()
71
78
  self.add(axes, curve1)
@@ -97,8 +104,8 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
97
104
 
98
105
  def __init__(
99
106
  self,
100
- function: Callable[[float, float], float],
101
- t_range: Sequence[float] | None = None,
107
+ function: Callable[[float], Point3DLike],
108
+ t_range: tuple[float, float] | tuple[float, float, float] = (0, 1),
102
109
  scaling: _ScaleBase = LinearBase(),
103
110
  dt: float = 1e-8,
104
111
  discontinuities: Iterable[float] | None = None,
@@ -106,10 +113,13 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
106
113
  use_vectorized: bool = False,
107
114
  **kwargs,
108
115
  ):
109
- self.function = function
110
- 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
111
121
  if len(t_range) == 2:
112
- t_range = np.array([*t_range, 0.01])
122
+ t_range = (*t_range, 0.01)
113
123
 
114
124
  self.scaling = scaling
115
125
 
@@ -121,13 +131,13 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
121
131
 
122
132
  super().__init__(**kwargs)
123
133
 
124
- def get_function(self):
134
+ def get_function(self) -> Callable[[float], Point3D]:
125
135
  return self.function
126
136
 
127
- def get_point_from_function(self, t):
137
+ def get_point_from_function(self, t: float) -> Point3D:
128
138
  return self.function(t)
129
139
 
130
- def generate_points(self):
140
+ def generate_points(self) -> Self:
131
141
  if self.discontinuities is not None:
132
142
  discontinuities = filter(
133
143
  lambda t: self.t_min <= t <= self.t_max,