manim 0.18.0__py3-none-any.whl → 0.18.1__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 (115) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +18 -10
  3. manim/_config/__init__.py +5 -2
  4. manim/_config/cli_colors.py +12 -8
  5. manim/_config/default.cfg +1 -1
  6. manim/_config/logger_utils.py +9 -8
  7. manim/_config/utils.py +637 -449
  8. manim/animation/animation.py +9 -2
  9. manim/animation/composition.py +78 -40
  10. manim/animation/creation.py +12 -6
  11. manim/animation/fading.py +0 -1
  12. manim/animation/indication.py +10 -21
  13. manim/animation/movement.py +1 -2
  14. manim/animation/rotation.py +1 -1
  15. manim/animation/specialized.py +1 -1
  16. manim/animation/speedmodifier.py +7 -2
  17. manim/animation/transform_matching_parts.py +1 -1
  18. manim/camera/camera.py +13 -4
  19. manim/cli/cfg/group.py +18 -8
  20. manim/cli/checkhealth/checks.py +2 -0
  21. manim/cli/checkhealth/commands.py +2 -0
  22. manim/cli/default_group.py +13 -5
  23. manim/cli/init/commands.py +4 -1
  24. manim/cli/plugins/commands.py +3 -0
  25. manim/cli/render/commands.py +27 -20
  26. manim/cli/render/ease_of_access_options.py +4 -3
  27. manim/cli/render/global_options.py +9 -7
  28. manim/cli/render/output_options.py +6 -5
  29. manim/cli/render/render_options.py +13 -13
  30. manim/constants.py +54 -15
  31. manim/gui/gui.py +2 -0
  32. manim/mobject/geometry/arc.py +4 -4
  33. manim/mobject/geometry/boolean_ops.py +13 -9
  34. manim/mobject/geometry/line.py +16 -8
  35. manim/mobject/geometry/polygram.py +17 -5
  36. manim/mobject/geometry/tips.py +2 -2
  37. manim/mobject/graph.py +379 -106
  38. manim/mobject/graphing/coordinate_systems.py +17 -20
  39. manim/mobject/graphing/functions.py +14 -10
  40. manim/mobject/graphing/number_line.py +1 -1
  41. manim/mobject/mobject.py +175 -72
  42. manim/mobject/opengl/opengl_compatibility.py +2 -0
  43. manim/mobject/opengl/opengl_geometry.py +26 -1
  44. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  45. manim/mobject/opengl/opengl_mobject.py +3 -0
  46. manim/mobject/opengl/opengl_point_cloud_mobject.py +2 -0
  47. manim/mobject/opengl/opengl_surface.py +2 -0
  48. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  49. manim/mobject/opengl/opengl_vectorized_mobject.py +19 -14
  50. manim/mobject/svg/brace.py +2 -0
  51. manim/mobject/svg/svg_mobject.py +10 -12
  52. manim/mobject/table.py +0 -1
  53. manim/mobject/text/code_mobject.py +2 -0
  54. manim/mobject/text/numbers.py +2 -0
  55. manim/mobject/text/tex_mobject.py +1 -1
  56. manim/mobject/text/text_mobject.py +43 -6
  57. manim/mobject/three_d/three_d_utils.py +4 -4
  58. manim/mobject/three_d/three_dimensions.py +4 -4
  59. manim/mobject/types/image_mobject.py +5 -1
  60. manim/mobject/types/point_cloud_mobject.py +2 -0
  61. manim/mobject/types/vectorized_mobject.py +124 -29
  62. manim/mobject/value_tracker.py +3 -3
  63. manim/mobject/vector_field.py +3 -1
  64. manim/plugins/__init__.py +15 -1
  65. manim/plugins/plugins_flags.py +11 -5
  66. manim/renderer/cairo_renderer.py +12 -2
  67. manim/renderer/opengl_renderer.py +2 -3
  68. manim/renderer/opengl_renderer_window.py +2 -0
  69. manim/renderer/shader_wrapper.py +2 -0
  70. manim/renderer/vectorized_mobject_rendering.py +5 -0
  71. manim/scene/scene.py +22 -6
  72. manim/scene/scene_file_writer.py +3 -1
  73. manim/scene/section.py +2 -0
  74. manim/scene/three_d_scene.py +5 -6
  75. manim/scene/vector_space_scene.py +21 -5
  76. manim/typing.py +567 -67
  77. manim/utils/bezier.py +9 -18
  78. manim/utils/caching.py +2 -0
  79. manim/utils/color/BS381.py +1 -0
  80. manim/utils/color/XKCD.py +1 -0
  81. manim/utils/color/core.py +31 -13
  82. manim/utils/commands.py +8 -1
  83. manim/utils/debug.py +0 -1
  84. manim/utils/deprecation.py +3 -2
  85. manim/utils/docbuild/__init__.py +17 -0
  86. manim/utils/docbuild/autoaliasattr_directive.py +197 -0
  87. manim/utils/docbuild/autocolor_directive.py +9 -4
  88. manim/utils/docbuild/manim_directive.py +18 -9
  89. manim/utils/docbuild/module_parsing.py +198 -0
  90. manim/utils/exceptions.py +6 -0
  91. manim/utils/family.py +2 -0
  92. manim/utils/family_ops.py +5 -0
  93. manim/utils/file_ops.py +6 -2
  94. manim/utils/hashing.py +2 -0
  95. manim/utils/ipython_magic.py +2 -0
  96. manim/utils/module_ops.py +2 -0
  97. manim/utils/opengl.py +14 -0
  98. manim/utils/parameter_parsing.py +31 -0
  99. manim/utils/paths.py +12 -20
  100. manim/utils/rate_functions.py +6 -8
  101. manim/utils/space_ops.py +81 -36
  102. manim/utils/testing/__init__.py +17 -0
  103. manim/utils/testing/frames_comparison.py +7 -5
  104. manim/utils/tex.py +124 -196
  105. manim/utils/tex_file_writing.py +2 -0
  106. manim/utils/tex_templates.py +1 -0
  107. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/LICENSE.community +1 -1
  108. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/METADATA +29 -35
  109. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/RECORD +112 -112
  110. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/WHEEL +1 -1
  111. manim/cli/new/__init__.py +0 -0
  112. manim/cli/new/group.py +0 -189
  113. manim/plugins/import_plugins.py +0 -43
  114. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/LICENSE +0 -0
  115. {manim-0.18.0.dist-info → manim-0.18.1.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,5 @@
1
1
  """Animate mobjects."""
2
2
 
3
-
4
3
  from __future__ import annotations
5
4
 
6
5
  from manim.mobject.opengl.opengl_mobject import OpenGLMobject
@@ -18,6 +17,8 @@ __all__ = ["Animation", "Wait", "override_animation"]
18
17
  from copy import deepcopy
19
18
  from typing import TYPE_CHECKING, Callable, Iterable, Sequence
20
19
 
20
+ from typing_extensions import Self
21
+
21
22
  if TYPE_CHECKING:
22
23
  from manim.scene.scene import Scene
23
24
 
@@ -112,7 +113,7 @@ class Animation:
112
113
  *args,
113
114
  use_override=True,
114
115
  **kwargs,
115
- ):
116
+ ) -> Self:
116
117
  if isinstance(mobject, Mobject) and use_override:
117
118
  func = mobject.animation_override_for(cls)
118
119
  if func is not None:
@@ -191,6 +192,11 @@ class Animation:
191
192
  method.
192
193
 
193
194
  """
195
+ if self.run_time <= 0:
196
+ raise ValueError(
197
+ f"{self} has a run_time of <= 0 seconds, this cannot be rendered correctly. "
198
+ "Please set the run_time to be positive"
199
+ )
194
200
  self.starting_mobject = self.create_starting_mobject()
195
201
  if self.suspend_mobject_updating:
196
202
  # All calls to self.mobject's internal updaters
@@ -398,6 +404,7 @@ class Animation:
398
404
  self.run_time = run_time
399
405
  return self
400
406
 
407
+ # TODO: is this getter even necessary?
401
408
  def get_run_time(self) -> float:
402
409
  """Get the run time of the animation.
403
410
 
@@ -1,26 +1,25 @@
1
1
  """Tools for displaying multiple animations at once."""
2
2
 
3
-
4
3
  from __future__ import annotations
5
4
 
6
- from typing import TYPE_CHECKING, Callable, Sequence
5
+ import types
6
+ from typing import TYPE_CHECKING, Callable, Iterable, Sequence
7
7
 
8
8
  import numpy as np
9
9
 
10
+ from manim._config import config
11
+ from manim.animation.animation import Animation, prepare_animation
12
+ from manim.constants import RendererType
13
+ from manim.mobject.mobject import Group, Mobject
10
14
  from manim.mobject.opengl.opengl_mobject import OpenGLGroup
11
-
12
- from .._config import config
13
- from ..animation.animation import Animation, prepare_animation
14
- from ..constants import RendererType
15
- from ..mobject.mobject import Group, Mobject
16
- from ..scene.scene import Scene
17
- from ..utils.iterables import remove_list_redundancies
18
- from ..utils.rate_functions import linear
15
+ from manim.scene.scene import Scene
16
+ from manim.utils.iterables import remove_list_redundancies
17
+ from manim.utils.parameter_parsing import flatten_iterable_parameters
18
+ from manim.utils.rate_functions import linear
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup
22
-
23
- from ..mobject.types.vectorized_mobject import VGroup
22
+ from manim.mobject.types.vectorized_mobject import VGroup
24
23
 
25
24
  __all__ = ["AnimationGroup", "Succession", "LaggedStart", "LaggedStartMap"]
26
25
 
@@ -54,14 +53,15 @@ class AnimationGroup(Animation):
54
53
 
55
54
  def __init__(
56
55
  self,
57
- *animations: Animation,
56
+ *animations: Animation | Iterable[Animation] | types.GeneratorType[Animation],
58
57
  group: Group | VGroup | OpenGLGroup | OpenGLVGroup = None,
59
58
  run_time: float | None = None,
60
59
  rate_func: Callable[[float], float] = linear,
61
60
  lag_ratio: float = 0,
62
61
  **kwargs,
63
62
  ) -> None:
64
- self.animations = [prepare_animation(anim) for anim in animations]
63
+ arg_anim = flatten_iterable_parameters(animations)
64
+ self.animations = [prepare_animation(anim) for anim in arg_anim]
65
65
  self.rate_func = rate_func
66
66
  self.group = group
67
67
  if self.group is None:
@@ -81,6 +81,17 @@ class AnimationGroup(Animation):
81
81
  return list(self.group)
82
82
 
83
83
  def begin(self) -> None:
84
+ if self.run_time <= 0:
85
+ tmp = (
86
+ "Please set the run_time to be positive"
87
+ if len(self.animations) != 0
88
+ else "Please add at least one Animation with positive run_time"
89
+ )
90
+ raise ValueError(
91
+ f"{self} has a run_time of 0 seconds, this cannot be "
92
+ f"rendered correctly. {tmp}."
93
+ )
94
+ self.anim_group_time = 0.0
84
95
  if self.suspend_mobject_updating:
85
96
  self.group.suspend_updating()
86
97
  for anim in self.animations:
@@ -91,8 +102,9 @@ class AnimationGroup(Animation):
91
102
  anim._setup_scene(scene)
92
103
 
93
104
  def finish(self) -> None:
94
- for anim in self.animations:
95
- anim.finish()
105
+ self.interpolate(1)
106
+ self.anims_begun[:] = True
107
+ self.anims_finished[:] = True
96
108
  if self.suspend_mobject_updating:
97
109
  self.group.resume_updating()
98
110
 
@@ -104,7 +116,9 @@ class AnimationGroup(Animation):
104
116
  anim.clean_up_from_scene(scene)
105
117
 
106
118
  def update_mobjects(self, dt: float) -> None:
107
- for anim in self.animations:
119
+ for anim in self.anims_with_timings["anim"][
120
+ self.anims_begun & ~self.anims_finished
121
+ ]:
108
122
  anim.update_mobjects(dt)
109
123
 
110
124
  def init_run_time(self, run_time) -> float:
@@ -121,22 +135,30 @@ class AnimationGroup(Animation):
121
135
  The duration of the animation in seconds.
122
136
  """
123
137
  self.build_animations_with_timings()
124
- if self.anims_with_timings:
125
- self.max_end_time = np.max([awt[2] for awt in self.anims_with_timings])
126
- else:
127
- self.max_end_time = 0
138
+ # Note: if lag_ratio < 1, then not necessarily the final animation's
139
+ # end time will be the max end time! Therefore we must calculate the
140
+ # maximum over all the end times, and not just take the last one.
141
+ # Example: if you want to play 2 animations of 10s and 1s with a
142
+ # lag_ratio of 0.1, the 1st one will end at t=10 and the 2nd one will
143
+ # end at t=2, so the AnimationGroup will end at t=10.
144
+ self.max_end_time = max(self.anims_with_timings["end"], default=0)
128
145
  return self.max_end_time if run_time is None else run_time
129
146
 
130
147
  def build_animations_with_timings(self) -> None:
131
148
  """Creates a list of triplets of the form (anim, start_time, end_time)."""
132
- self.anims_with_timings = []
133
- curr_time: float = 0
134
- for anim in self.animations:
135
- start_time: float = curr_time
136
- end_time: float = start_time + anim.get_run_time()
137
- self.anims_with_timings.append((anim, start_time, end_time))
138
- # Start time of next animation is based on the lag_ratio
139
- curr_time = (1 - self.lag_ratio) * start_time + self.lag_ratio * end_time
149
+ run_times = np.array([anim.run_time for anim in self.animations])
150
+ num_animations = run_times.shape[0]
151
+ dtype = [("anim", "O"), ("start", "f8"), ("end", "f8")]
152
+ self.anims_with_timings = np.zeros(num_animations, dtype=dtype)
153
+ self.anims_begun = np.zeros(num_animations, dtype=bool)
154
+ self.anims_finished = np.zeros(num_animations, dtype=bool)
155
+ if num_animations == 0:
156
+ return
157
+
158
+ lags = run_times[:-1] * self.lag_ratio
159
+ self.anims_with_timings["anim"] = self.animations
160
+ self.anims_with_timings["start"][1:] = np.add.accumulate(lags)
161
+ self.anims_with_timings["end"] = self.anims_with_timings["start"] + run_times
140
162
 
141
163
  def interpolate(self, alpha: float) -> None:
142
164
  # Note, if the run_time of AnimationGroup has been
@@ -144,14 +166,30 @@ class AnimationGroup(Animation):
144
166
  # times might not correspond to actual times,
145
167
  # e.g. of the surrounding scene. Instead they'd
146
168
  # be a rescaled version. But that's okay!
147
- time = self.rate_func(alpha) * self.max_end_time
148
- for anim, start_time, end_time in self.anims_with_timings:
149
- anim_time = end_time - start_time
150
- if anim_time == 0:
151
- sub_alpha = 0
152
- else:
153
- sub_alpha = np.clip((time - start_time) / anim_time, 0, 1)
154
- anim.interpolate(sub_alpha)
169
+ anim_group_time = self.rate_func(alpha) * self.max_end_time
170
+ time_goes_back = anim_group_time < self.anim_group_time
171
+
172
+ # Only update ongoing animations
173
+ awt = self.anims_with_timings
174
+ new_begun = anim_group_time >= awt["start"]
175
+ new_finished = anim_group_time > awt["end"]
176
+ to_update = awt[
177
+ (self.anims_begun | new_begun) & (~self.anims_finished | ~new_finished)
178
+ ]
179
+
180
+ run_times = to_update["end"] - to_update["start"]
181
+ sub_alphas = (anim_group_time - to_update["start"]) / run_times
182
+ if time_goes_back:
183
+ sub_alphas[sub_alphas < 0] = 0
184
+ else:
185
+ sub_alphas[sub_alphas > 1] = 1
186
+
187
+ for anim_to_update, sub_alpha in zip(to_update["anim"], sub_alphas):
188
+ anim_to_update.interpolate(sub_alpha)
189
+
190
+ self.anim_group_time = anim_group_time
191
+ self.anims_begun = new_begun
192
+ self.anims_finished = new_finished
155
193
 
156
194
 
157
195
  class Succession(AnimationGroup):
@@ -226,8 +264,8 @@ class Succession(AnimationGroup):
226
264
  self.active_animation = self.animations[index]
227
265
  self.active_animation._setup_scene(self.scene)
228
266
  self.active_animation.begin()
229
- self.active_start_time = self.anims_with_timings[index][1]
230
- self.active_end_time = self.anims_with_timings[index][2]
267
+ self.active_start_time = self.anims_with_timings[index]["start"]
268
+ self.active_end_time = self.anims_with_timings[index]["end"]
231
269
 
232
270
  def next_animation(self) -> None:
233
271
  """Proceeds to the next animation.
@@ -244,7 +282,7 @@ class Succession(AnimationGroup):
244
282
  self.next_animation()
245
283
  if self.active_animation is not None and self.active_start_time is not None:
246
284
  elapsed = current_time - self.active_start_time
247
- active_run_time = self.active_animation.get_run_time()
285
+ active_run_time = self.active_animation.run_time
248
286
  subalpha = elapsed / active_run_time if active_run_time != 0.0 else 1.0
249
287
  self.active_animation.interpolate(subalpha)
250
288
 
@@ -456,7 +456,7 @@ class SpiralIn(Animation):
456
456
  fade_in_fraction=0.3,
457
457
  **kwargs,
458
458
  ) -> None:
459
- self.shapes = shapes
459
+ self.shapes = shapes.copy()
460
460
  self.scale_factor = scale_factor
461
461
  self.shape_center = shapes.get_center()
462
462
  self.fade_in_fraction = fade_in_fraction
@@ -473,15 +473,21 @@ class SpiralIn(Animation):
473
473
 
474
474
  def interpolate_mobject(self, alpha: float) -> None:
475
475
  alpha = self.rate_func(alpha)
476
- for shape in self.shapes:
476
+ for original_shape, shape in zip(self.shapes, self.mobject):
477
477
  shape.restore()
478
- shape.save_state()
479
- opacity = shape.get_fill_opacity()
480
- new_opacity = min(opacity, alpha * opacity / self.fade_in_fraction)
478
+ fill_opacity = original_shape.get_fill_opacity()
479
+ stroke_opacity = original_shape.get_stroke_opacity()
480
+ new_fill_opacity = min(
481
+ fill_opacity, alpha * fill_opacity / self.fade_in_fraction
482
+ )
483
+ new_stroke_opacity = min(
484
+ stroke_opacity, alpha * stroke_opacity / self.fade_in_fraction
485
+ )
481
486
  shape.shift((shape.final_position - shape.initial_position) * alpha)
482
487
  shape.rotate(TAU * alpha, about_point=self.shape_center)
483
488
  shape.rotate(-TAU * alpha, about_point=shape.get_center_of_mass())
484
- shape.set_opacity(new_opacity)
489
+ shape.set_fill(opacity=new_fill_opacity)
490
+ shape.set_stroke(opacity=new_stroke_opacity)
485
491
 
486
492
 
487
493
  class ShowIncreasingSubsets(Animation):
manim/animation/fading.py CHANGED
@@ -12,7 +12,6 @@
12
12
 
13
13
  """
14
14
 
15
-
16
15
  from __future__ import annotations
17
16
 
18
17
  __all__ = [
@@ -31,7 +31,6 @@ __all__ = [
31
31
  "Flash",
32
32
  "ShowPassingFlash",
33
33
  "ShowPassingFlashWithThinningStrokeWidth",
34
- "ShowCreationThenFadeOut",
35
34
  "ApplyWave",
36
35
  "Circumscribe",
37
36
  "Wiggle",
@@ -131,7 +130,7 @@ class Indicate(Transform):
131
130
  color
132
131
  The color the mobject temporally takes.
133
132
  rate_func
134
- The function definig the animation progress at every point in time.
133
+ The function defining the animation progress at every point in time.
135
134
  kwargs
136
135
  Additional arguments to be passed to the :class:`~.Succession` constructor
137
136
 
@@ -148,7 +147,7 @@ class Indicate(Transform):
148
147
 
149
148
  def __init__(
150
149
  self,
151
- mobject: "Mobject",
150
+ mobject: Mobject,
152
151
  scale_factor: float = 1.2,
153
152
  color: str = YELLOW,
154
153
  rate_func: Callable[[float, Optional[float]], np.ndarray] = there_and_back,
@@ -158,7 +157,7 @@ class Indicate(Transform):
158
157
  self.scale_factor = scale_factor
159
158
  super().__init__(mobject, rate_func=rate_func, **kwargs)
160
159
 
161
- def create_target(self) -> "Mobject":
160
+ def create_target(self) -> Mobject:
162
161
  target = self.mobject.copy()
163
162
  target.scale(self.scale_factor)
164
163
  target.set_color(self.color)
@@ -342,16 +341,6 @@ class ShowPassingFlashWithThinningStrokeWidth(AnimationGroup):
342
341
  )
343
342
 
344
343
 
345
- @deprecated(
346
- since="v0.15.0",
347
- until="v0.16.0",
348
- message="Use Create then FadeOut to achieve this effect.",
349
- )
350
- class ShowCreationThenFadeOut(Succession):
351
- def __init__(self, mobject: "Mobject", remover: bool = True, **kwargs) -> None:
352
- super().__init__(Create(mobject), FadeOut(mobject), remover=remover, **kwargs)
353
-
354
-
355
344
  class ApplyWave(Homotopy):
356
345
  """Send a wave through the Mobject distorting it temporarily.
357
346
 
@@ -397,7 +386,7 @@ class ApplyWave(Homotopy):
397
386
 
398
387
  def __init__(
399
388
  self,
400
- mobject: "Mobject",
389
+ mobject: Mobject,
401
390
  direction: np.ndarray = UP,
402
391
  amplitude: float = 0.2,
403
392
  wave_func: Callable[[float], float] = smooth,
@@ -415,7 +404,7 @@ class ApplyWave(Homotopy):
415
404
  # This wave is build up as follows:
416
405
  # The time is split into 2*ripples phases. In every phase the amplitude
417
406
  # either rises to one or goes down to zero. Consecutive ripples will have
418
- # their amplitudes in oppising directions (first ripple from 0 to 1 to 0,
407
+ # their amplitudes in opposing directions (first ripple from 0 to 1 to 0,
419
408
  # second from 0 to -1 to 0 and so on). This is how two ripples would be
420
409
  # divided into phases:
421
410
 
@@ -454,7 +443,7 @@ class ApplyWave(Homotopy):
454
443
  return wave_func(t * phases)
455
444
  elif phase == phases - 1:
456
445
  # last ripple. Rising or falling depending on the number of ripples
457
- # The (ripples % 2)-term is used to make this destinction.
446
+ # The (ripples % 2)-term is used to make this distinction.
458
447
  t -= phase / phases # Time relative to the phase
459
448
  return (1 - wave_func(t * phases)) * (2 * (ripples % 2) - 1)
460
449
  else:
@@ -516,7 +505,7 @@ class Wiggle(Animation):
516
505
 
517
506
  def __init__(
518
507
  self,
519
- mobject: "Mobject",
508
+ mobject: Mobject,
520
509
  scale_value: float = 1.1,
521
510
  rotation_angle: float = 0.01 * TAU,
522
511
  n_wiggles: int = 6,
@@ -544,8 +533,8 @@ class Wiggle(Animation):
544
533
 
545
534
  def interpolate_submobject(
546
535
  self,
547
- submobject: "Mobject",
548
- starting_submobject: "Mobject",
536
+ submobject: Mobject,
537
+ starting_submobject: Mobject,
549
538
  alpha: float,
550
539
  ) -> None:
551
540
  submobject.points[:, :] = starting_submobject.points
@@ -567,7 +556,7 @@ class Circumscribe(Succession):
567
556
  mobject
568
557
  The mobject to be circumscribed.
569
558
  shape
570
- The shape with which to surrond the given mobject. Should be either
559
+ The shape with which to surround the given mobject. Should be either
571
560
  :class:`~.Rectangle` or :class:`~.Circle`
572
561
  fade_in
573
562
  Whether to make the surrounding shape to fade in. It will be drawn otherwise.
@@ -136,8 +136,7 @@ class PhaseFlow(Animation):
136
136
 
137
137
  class MoveAlongPath(Animation):
138
138
  """Make one mobject move along the path of another mobject.
139
- Example
140
- --------
139
+
141
140
  .. manim:: MoveAlongPathExample
142
141
 
143
142
  class MoveAlongPathExample(Scene):
@@ -59,7 +59,7 @@ class Rotate(Transform):
59
59
  about_point
60
60
  The rotation center.
61
61
  about_edge
62
- If ``about_point``is ``None``, this argument specifies
62
+ If ``about_point`` is ``None``, this argument specifies
63
63
  the direction of the bounding box point to be taken as
64
64
  the rotation center.
65
65
 
@@ -84,7 +84,7 @@ class Broadcast(LaggedStart):
84
84
 
85
85
  mob.move_to(self.focal_point)
86
86
  mob.save_state()
87
- mob.set_width(self.initial_width)
87
+ mob.set(width=self.initial_width)
88
88
 
89
89
  if fill_o:
90
90
  mob.set_opacity(self.initial_opacity)
@@ -4,15 +4,20 @@ from __future__ import annotations
4
4
 
5
5
  import inspect
6
6
  import types
7
- from typing import Callable
7
+ from typing import TYPE_CHECKING, Callable
8
8
 
9
9
  from numpy import piecewise
10
10
 
11
11
  from ..animation.animation import Animation, Wait, prepare_animation
12
12
  from ..animation.composition import AnimationGroup
13
- from ..mobject.mobject import Mobject, Updater, _AnimationBuilder
13
+ from ..mobject.mobject import Mobject, _AnimationBuilder
14
14
  from ..scene.scene import Scene
15
15
 
16
+ if TYPE_CHECKING:
17
+ from ..mobject.mobject import Updater
18
+
19
+ __all__ = ["ChangeSpeed"]
20
+
16
21
 
17
22
  class ChangeSpeed(Animation):
18
23
  """Modifies the speed of passed animation.
@@ -225,7 +225,7 @@ class TransformMatchingShapes(TransformMatchingAbstractBase):
225
225
  def get_mobject_key(mobject: Mobject) -> int:
226
226
  mobject.save_state()
227
227
  mobject.center()
228
- mobject.set_height(1)
228
+ mobject.set(height=1)
229
229
  result = hash(np.round(mobject.points, 3).tobytes())
230
230
  mobject.restore()
231
231
  return result
manim/camera/camera.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """A camera converts the mobjects contained in a Scene into an array of pixels."""
2
2
 
3
-
4
3
  from __future__ import annotations
5
4
 
6
5
  __all__ = ["Camera", "BackgroundColoredVMobjectDisplayer"]
@@ -37,6 +36,14 @@ LINE_JOIN_MAP = {
37
36
  }
38
37
 
39
38
 
39
+ CAP_STYLE_MAP = {
40
+ CapStyleType.AUTO: None, # TODO: this could be improved
41
+ CapStyleType.ROUND: cairo.LineCap.ROUND,
42
+ CapStyleType.BUTT: cairo.LineCap.BUTT,
43
+ CapStyleType.SQUARE: cairo.LineCap.SQUARE,
44
+ }
45
+
46
+
40
47
  class Camera:
41
48
  """Base camera class.
42
49
 
@@ -778,11 +785,13 @@ class Camera:
778
785
  ctx.set_line_width(
779
786
  width
780
787
  * self.cairo_line_width_multiple
781
- # This ensures lines have constant width as you zoom in on them.
782
788
  * (self.frame_width / self.frame_width),
789
+ # This ensures lines have constant width as you zoom in on them.
783
790
  )
784
791
  if vmobject.joint_type != LineJointType.AUTO:
785
792
  ctx.set_line_join(LINE_JOIN_MAP[vmobject.joint_type])
793
+ if vmobject.cap_style != CapStyleType.AUTO:
794
+ ctx.set_line_cap(CAP_STYLE_MAP[vmobject.cap_style])
786
795
  ctx.stroke_preserve()
787
796
  return self
788
797
 
@@ -973,8 +982,8 @@ class Camera:
973
982
  sub_image = Image.fromarray(image_mobject.get_pixel_array(), mode="RGBA")
974
983
 
975
984
  # Reshape
976
- pixel_width = max(int(pdist([ul_coords, ur_coords])), 1)
977
- pixel_height = max(int(pdist([ul_coords, dl_coords])), 1)
985
+ pixel_width = max(int(pdist([ul_coords, ur_coords]).item()), 1)
986
+ pixel_height = max(int(pdist([ul_coords, dl_coords]).item()), 1)
978
987
  sub_image = sub_image.resize(
979
988
  (pixel_width, pixel_height),
980
989
  resample=image_mobject.resampling_algorithm,
manim/cli/cfg/group.py CHANGED
@@ -5,13 +5,12 @@ cfg``. Here you can specify options, subcommands, and subgroups for the cfg
5
5
  group.
6
6
 
7
7
  """
8
+
8
9
  from __future__ import annotations
9
10
 
10
- import os
11
11
  from ast import literal_eval
12
12
  from pathlib import Path
13
13
 
14
- import click
15
14
  import cloup
16
15
  from rich.errors import StyleSyntaxError
17
16
  from rich.style import Style
@@ -28,6 +27,17 @@ If left empty, the default colour will be used.[/red]
28
27
  """
29
28
  RICH_NON_STYLE_ENTRIES: str = ["log.width", "log.height", "log.timestamps"]
30
29
 
30
+ __all__ = [
31
+ "value_from_string",
32
+ "value_from_string",
33
+ "is_valid_style",
34
+ "replace_keys",
35
+ "cfg",
36
+ "write",
37
+ "show",
38
+ "export",
39
+ ]
40
+
31
41
 
32
42
  def value_from_string(value: str) -> str | int | bool:
33
43
  """Extracts the literal of proper datatype from a string.
@@ -123,21 +133,21 @@ def replace_keys(default: dict) -> dict:
123
133
  epilog=EPILOG,
124
134
  help="Manages Manim configuration files.",
125
135
  )
126
- @click.pass_context
136
+ @cloup.pass_context
127
137
  def cfg(ctx):
128
138
  """Responsible for the cfg subcommand."""
129
139
  pass
130
140
 
131
141
 
132
142
  @cfg.command(context_settings=cli_ctx_settings, no_args_is_help=True)
133
- @click.option(
143
+ @cloup.option(
134
144
  "-l",
135
145
  "--level",
136
- type=click.Choice(["user", "cwd"], case_sensitive=False),
146
+ type=cloup.Choice(["user", "cwd"], case_sensitive=False),
137
147
  default="cwd",
138
148
  help="Specify if this config is for user or the working directory.",
139
149
  )
140
- @click.option("-o", "--open", "openfile", is_flag=True)
150
+ @cloup.option("-o", "--open", "openfile", is_flag=True)
141
151
  def write(level: str = None, openfile: bool = False) -> None:
142
152
  config_paths = config_file_paths()
143
153
  console.print(
@@ -258,8 +268,8 @@ def show():
258
268
 
259
269
 
260
270
  @cfg.command(context_settings=cli_ctx_settings)
261
- @click.option("-d", "--directory", default=Path.cwd())
262
- @click.pass_context
271
+ @cloup.option("-d", "--directory", default=Path.cwd())
272
+ @cloup.pass_context
263
273
  def export(ctx, directory):
264
274
  directory_path = Path(directory)
265
275
  if directory_path.absolute == Path.cwd().absolute:
@@ -10,6 +10,8 @@ from typing import Callable
10
10
 
11
11
  from ..._config import config
12
12
 
13
+ __all__ = ["HEALTH_CHECKS"]
14
+
13
15
  HEALTH_CHECKS = []
14
16
 
15
17
 
@@ -12,6 +12,8 @@ import cloup
12
12
 
13
13
  from .checks import HEALTH_CHECKS
14
14
 
15
+ __all__ = ["checkhealth"]
16
+
15
17
 
16
18
  @cloup.command(
17
19
  context_settings=None,
@@ -1,10 +1,18 @@
1
- """DefaultGroup allows a subcommand to act as the main command
1
+ """``DefaultGroup`` allows a subcommand to act as the main command.
2
2
 
3
3
  In particular, this class is what allows ``manim`` to act as ``manim render``.
4
+
5
+ .. note::
6
+ This is a vendored version of https://github.com/click-contrib/click-default-group/
7
+ under the BSD 3-Clause "New" or "Revised" License.
8
+
9
+ This library isn't used as a dependency as we need to inherit from ``cloup.Group`` instead
10
+ of ``click.Group``.
4
11
  """
5
- import cloup
6
12
 
7
- from .. import logger
13
+ import warnings
14
+
15
+ import cloup
8
16
 
9
17
  __all__ = ["DefaultGroup"]
10
18
 
@@ -54,8 +62,8 @@ class DefaultGroup(cloup.Group):
54
62
  decorator = super().command(*args, **kwargs)
55
63
  if not default:
56
64
  return decorator
57
- logger.log(
58
- "Use default param of DefaultGroup or " "set_default_command() instead",
65
+ warnings.warn(
66
+ "Use default param of DefaultGroup or set_default_command() instead",
59
67
  DeprecationWarning,
60
68
  )
61
69
 
@@ -5,6 +5,7 @@ init``. Here you can specify options, subcommands, and subgroups for the init
5
5
  group.
6
6
 
7
7
  """
8
+
8
9
  from __future__ import annotations
9
10
 
10
11
  import configparser
@@ -30,6 +31,8 @@ CFG_DEFAULTS = {
30
31
  "resolution": (854, 480),
31
32
  }
32
33
 
34
+ __all__ = ["select_resolution", "update_cfg", "project", "scene"]
35
+
33
36
 
34
37
  def select_resolution():
35
38
  """Prompts input of type click.Choice from user. Presents options from QUALITIES constant.
@@ -47,7 +50,7 @@ def select_resolution():
47
50
  resolution_options.pop()
48
51
  choice = click.prompt(
49
52
  "\nSelect resolution:\n",
50
- type=click.Choice([f"{i[0]}p" for i in resolution_options]),
53
+ type=cloup.Choice([f"{i[0]}p" for i in resolution_options]),
51
54
  show_default=False,
52
55
  default="480p",
53
56
  )
@@ -5,6 +5,7 @@ plugin``. Here you can specify options, subcommands, and subgroups for the plugi
5
5
  group.
6
6
 
7
7
  """
8
+
8
9
  from __future__ import annotations
9
10
 
10
11
  import cloup
@@ -12,6 +13,8 @@ import cloup
12
13
  from ...constants import CONTEXT_SETTINGS, EPILOG
13
14
  from ...plugins.plugins_flags import list_plugins
14
15
 
16
+ __all__ = ["plugins"]
17
+
15
18
 
16
19
  @cloup.command(
17
20
  context_settings=CONTEXT_SETTINGS,