manim 0.17.0__py3-none-any.whl → 0.19.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.
- manim/__init__.py +11 -6
- manim/__main__.py +62 -19
- manim/_config/__init__.py +10 -9
- manim/_config/cli_colors.py +26 -9
- manim/_config/default.cfg +1 -3
- manim/_config/logger_utils.py +23 -13
- manim/_config/utils.py +662 -468
- manim/animation/animation.py +164 -18
- manim/animation/changing.py +34 -23
- manim/animation/composition.py +265 -67
- manim/animation/creation.py +208 -26
- manim/animation/fading.py +16 -18
- manim/animation/growing.py +35 -15
- manim/animation/indication.py +150 -76
- manim/animation/movement.py +56 -22
- manim/animation/numbers.py +64 -6
- manim/animation/rotation.py +78 -7
- manim/animation/specialized.py +6 -7
- manim/animation/speedmodifier.py +13 -10
- manim/animation/transform.py +14 -11
- manim/animation/transform_matching_parts.py +3 -4
- manim/animation/updaters/mobject_update_utils.py +152 -30
- manim/animation/updaters/update.py +10 -7
- manim/camera/camera.py +182 -118
- manim/camera/mapping_camera.py +34 -3
- manim/camera/moving_camera.py +95 -74
- manim/camera/multi_camera.py +23 -15
- manim/camera/three_d_camera.py +70 -52
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +76 -44
- manim/cli/checkhealth/checks.py +192 -0
- manim/cli/checkhealth/commands.py +90 -0
- manim/cli/default_group.py +158 -25
- manim/cli/init/commands.py +33 -25
- manim/cli/plugins/commands.py +16 -3
- manim/cli/render/commands.py +72 -60
- manim/cli/render/ease_of_access_options.py +4 -3
- manim/cli/render/global_options.py +59 -17
- manim/cli/render/output_options.py +6 -5
- manim/cli/render/render_options.py +98 -33
- manim/constants.py +109 -59
- manim/data_structures.py +31 -0
- manim/mobject/frame.py +8 -5
- manim/mobject/geometry/__init__.py +1 -0
- manim/mobject/geometry/arc.py +277 -135
- manim/mobject/geometry/boolean_ops.py +32 -31
- manim/mobject/geometry/labeled.py +376 -0
- manim/mobject/geometry/line.py +192 -87
- manim/mobject/geometry/polygram.py +224 -58
- manim/mobject/geometry/shape_matchers.py +61 -25
- manim/mobject/geometry/tips.py +122 -48
- manim/mobject/graph.py +1027 -419
- manim/mobject/graphing/coordinate_systems.py +533 -278
- manim/mobject/graphing/functions.py +53 -32
- manim/mobject/graphing/number_line.py +123 -65
- manim/mobject/graphing/probability.py +88 -62
- manim/mobject/graphing/scale.py +33 -19
- manim/mobject/logo.py +118 -28
- manim/mobject/matrix.py +87 -83
- manim/mobject/mobject.py +912 -442
- manim/mobject/opengl/dot_cloud.py +16 -5
- manim/mobject/opengl/opengl_compatibility.py +4 -2
- manim/mobject/opengl/opengl_geometry.py +254 -153
- manim/mobject/opengl/opengl_image_mobject.py +3 -1
- manim/mobject/opengl/opengl_mobject.py +779 -482
- manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
- manim/mobject/opengl/opengl_surface.py +14 -92
- manim/mobject/opengl/opengl_three_dimensions.py +12 -8
- manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
- manim/mobject/svg/brace.py +173 -41
- manim/mobject/svg/svg_mobject.py +139 -53
- manim/mobject/table.py +61 -68
- manim/mobject/text/code_mobject.py +193 -539
- manim/mobject/text/numbers.py +81 -34
- manim/mobject/text/tex_mobject.py +130 -78
- manim/mobject/text/text_mobject.py +288 -164
- manim/mobject/three_d/polyhedra.py +111 -13
- manim/mobject/three_d/three_d_utils.py +17 -8
- manim/mobject/three_d/three_dimensions.py +239 -106
- manim/mobject/types/image_mobject.py +50 -30
- manim/mobject/types/point_cloud_mobject.py +120 -75
- manim/mobject/types/vectorized_mobject.py +841 -408
- manim/mobject/value_tracker.py +105 -38
- manim/mobject/vector_field.py +50 -31
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +14 -1
- manim/plugins/plugins_flags.py +10 -14
- manim/renderer/cairo_renderer.py +65 -50
- manim/renderer/opengl_renderer.py +89 -69
- manim/renderer/opengl_renderer_window.py +39 -18
- manim/renderer/shader.py +123 -87
- manim/renderer/shader_wrapper.py +44 -28
- manim/renderer/vectorized_mobject_rendering.py +38 -10
- manim/scene/moving_camera_scene.py +32 -3
- manim/scene/scene.py +507 -242
- manim/scene/scene_file_writer.py +371 -220
- manim/scene/section.py +20 -16
- manim/scene/three_d_scene.py +14 -22
- manim/scene/vector_space_scene.py +223 -129
- manim/scene/zoomed_scene.py +46 -41
- manim/typing.py +990 -0
- manim/utils/bezier.py +1823 -371
- manim/utils/caching.py +12 -5
- manim/utils/color/AS2700.py +236 -0
- manim/utils/color/BS381.py +318 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +533 -0
- manim/utils/color/XKCD.py +952 -0
- manim/utils/color/__init__.py +61 -0
- manim/utils/color/core.py +1667 -0
- manim/utils/color/manim_colors.py +218 -0
- manim/utils/commands.py +48 -20
- manim/utils/config_ops.py +39 -19
- manim/utils/debug.py +8 -7
- manim/utils/deprecation.py +86 -39
- manim/utils/docbuild/__init__.py +17 -0
- manim/utils/docbuild/autoaliasattr_directive.py +236 -0
- manim/utils/docbuild/autocolor_directive.py +99 -0
- manim/utils/docbuild/manim_directive.py +94 -41
- manim/utils/docbuild/module_parsing.py +245 -0
- manim/utils/exceptions.py +6 -0
- manim/utils/family.py +5 -3
- manim/utils/family_ops.py +17 -4
- manim/utils/file_ops.py +27 -17
- manim/utils/hashing.py +55 -45
- manim/utils/images.py +13 -7
- manim/utils/ipython_magic.py +13 -7
- manim/utils/iterables.py +163 -120
- manim/utils/module_ops.py +66 -24
- manim/utils/opengl.py +77 -24
- manim/utils/parameter_parsing.py +32 -0
- manim/utils/paths.py +30 -33
- manim/utils/polylabel.py +235 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +98 -32
- manim/utils/simple_functions.py +25 -33
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +188 -115
- manim/utils/testing/__init__.py +17 -0
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +34 -18
- manim/utils/testing/frames_comparison.py +37 -19
- manim/utils/tex.py +130 -198
- manim/utils/tex_file_writing.py +77 -47
- manim/utils/tex_templates.py +2 -1
- manim/utils/unit.py +6 -5
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
- manim-0.19.1.dist-info/RECORD +220 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
- manim-0.19.1.dist-info/entry_points.txt +3 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
- manim/cli/new/group.py +0 -189
- manim/communitycolors.py +0 -9
- manim/gui/__init__.py +0 -0
- manim/gui/gui.py +0 -82
- manim/plugins/import_plugins.py +0 -43
- manim/utils/color.py +0 -552
- manim-0.17.0.dist-info/RECORD +0 -206
- manim-0.17.0.dist-info/entry_points.txt +0 -4
- /manim/cli/{new → checkhealth}/__init__.py +0 -0
- {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE +0 -0
manim/mobject/geometry/arc.py
CHANGED
|
@@ -40,22 +40,23 @@ __all__ = [
|
|
|
40
40
|
"CubicBezier",
|
|
41
41
|
"ArcPolygon",
|
|
42
42
|
"ArcPolygonFromArcs",
|
|
43
|
+
"TangentialArc",
|
|
43
44
|
]
|
|
44
45
|
|
|
45
46
|
import itertools
|
|
46
|
-
import math
|
|
47
47
|
import warnings
|
|
48
|
-
from typing import TYPE_CHECKING,
|
|
48
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
49
49
|
|
|
50
50
|
import numpy as np
|
|
51
|
-
from
|
|
51
|
+
from typing_extensions import Self
|
|
52
52
|
|
|
53
53
|
from manim.constants import *
|
|
54
54
|
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
55
|
-
from manim.mobject.types.vectorized_mobject import VMobject
|
|
56
|
-
from manim.utils.color import
|
|
55
|
+
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
|
|
56
|
+
from manim.utils.color import BLACK, BLUE, RED, WHITE, ParsableManimColor
|
|
57
57
|
from manim.utils.iterables import adjacent_pairs
|
|
58
58
|
from manim.utils.space_ops import (
|
|
59
|
+
angle_between_vectors,
|
|
59
60
|
angle_of_vector,
|
|
60
61
|
cartesian_to_spherical,
|
|
61
62
|
line_intersection,
|
|
@@ -64,9 +65,19 @@ from manim.utils.space_ops import (
|
|
|
64
65
|
)
|
|
65
66
|
|
|
66
67
|
if TYPE_CHECKING:
|
|
68
|
+
from collections.abc import Iterable
|
|
69
|
+
|
|
70
|
+
import manim.mobject.geometry.tips as tips
|
|
71
|
+
from manim.mobject.geometry.line import Line
|
|
67
72
|
from manim.mobject.mobject import Mobject
|
|
68
73
|
from manim.mobject.text.tex_mobject import SingleStringMathTex, Tex
|
|
69
74
|
from manim.mobject.text.text_mobject import Text
|
|
75
|
+
from manim.typing import (
|
|
76
|
+
Point3D,
|
|
77
|
+
Point3DLike,
|
|
78
|
+
QuadraticSpline,
|
|
79
|
+
Vector3DLike,
|
|
80
|
+
)
|
|
70
81
|
|
|
71
82
|
|
|
72
83
|
class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
@@ -89,21 +100,26 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
89
100
|
|
|
90
101
|
def __init__(
|
|
91
102
|
self,
|
|
92
|
-
tip_length=DEFAULT_ARROW_TIP_LENGTH,
|
|
93
|
-
normal_vector=OUT,
|
|
94
|
-
tip_style={},
|
|
95
|
-
**kwargs,
|
|
96
|
-
):
|
|
97
|
-
self.tip_length = tip_length
|
|
103
|
+
tip_length: float = DEFAULT_ARROW_TIP_LENGTH,
|
|
104
|
+
normal_vector: Vector3DLike = OUT,
|
|
105
|
+
tip_style: dict = {},
|
|
106
|
+
**kwargs: Any,
|
|
107
|
+
) -> None:
|
|
108
|
+
self.tip_length: float = tip_length
|
|
98
109
|
self.normal_vector = normal_vector
|
|
99
|
-
self.tip_style = tip_style
|
|
110
|
+
self.tip_style: dict = tip_style
|
|
100
111
|
super().__init__(**kwargs)
|
|
101
112
|
|
|
102
113
|
# Adding, Creating, Modifying tips
|
|
103
114
|
|
|
104
115
|
def add_tip(
|
|
105
|
-
self,
|
|
106
|
-
|
|
116
|
+
self,
|
|
117
|
+
tip: tips.ArrowTip | None = None,
|
|
118
|
+
tip_shape: type[tips.ArrowTip] | None = None,
|
|
119
|
+
tip_length: float | None = None,
|
|
120
|
+
tip_width: float | None = None,
|
|
121
|
+
at_start: bool = False,
|
|
122
|
+
) -> Self:
|
|
107
123
|
"""Adds a tip to the TipableVMobject instance, recognising
|
|
108
124
|
that the endpoints might need to be switched if it's
|
|
109
125
|
a 'starting tip' or not.
|
|
@@ -118,8 +134,12 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
118
134
|
return self
|
|
119
135
|
|
|
120
136
|
def create_tip(
|
|
121
|
-
self,
|
|
122
|
-
|
|
137
|
+
self,
|
|
138
|
+
tip_shape: type[tips.ArrowTip] | None = None,
|
|
139
|
+
tip_length: float | None = None,
|
|
140
|
+
tip_width: float | None = None,
|
|
141
|
+
at_start: bool = False,
|
|
142
|
+
) -> tips.ArrowTip:
|
|
123
143
|
"""Stylises the tip, positions it spatially, and returns
|
|
124
144
|
the newly instantiated tip to the caller.
|
|
125
145
|
"""
|
|
@@ -127,13 +147,18 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
127
147
|
self.position_tip(tip, at_start)
|
|
128
148
|
return tip
|
|
129
149
|
|
|
130
|
-
def get_unpositioned_tip(
|
|
150
|
+
def get_unpositioned_tip(
|
|
151
|
+
self,
|
|
152
|
+
tip_shape: type[tips.ArrowTip] | None = None,
|
|
153
|
+
tip_length: float | None = None,
|
|
154
|
+
tip_width: float | None = None,
|
|
155
|
+
) -> tips.ArrowTip | tips.ArrowTriangleFilledTip:
|
|
131
156
|
"""Returns a tip that has been stylistically configured,
|
|
132
157
|
but has not yet been given a position in space.
|
|
133
158
|
"""
|
|
134
159
|
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
|
|
135
160
|
|
|
136
|
-
style = {}
|
|
161
|
+
style: dict[str, Any] = {}
|
|
137
162
|
|
|
138
163
|
if tip_shape is None:
|
|
139
164
|
tip_shape = ArrowTriangleFilledTip
|
|
@@ -151,7 +176,7 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
151
176
|
tip = tip_shape(length=tip_length, **style)
|
|
152
177
|
return tip
|
|
153
178
|
|
|
154
|
-
def position_tip(self, tip, at_start=False):
|
|
179
|
+
def position_tip(self, tip: tips.ArrowTip, at_start: bool = False) -> tips.ArrowTip:
|
|
155
180
|
# Last two control points, defining both
|
|
156
181
|
# the end, and the tangency direction
|
|
157
182
|
if at_start:
|
|
@@ -165,11 +190,13 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
165
190
|
angles[1] - PI - tip.tip_angle,
|
|
166
191
|
) # Rotates the tip along the azimuthal
|
|
167
192
|
if not hasattr(self, "_init_positioning_axis"):
|
|
168
|
-
axis =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
193
|
+
axis = np.array(
|
|
194
|
+
[
|
|
195
|
+
np.sin(angles[1]),
|
|
196
|
+
-np.cos(angles[1]),
|
|
197
|
+
0,
|
|
198
|
+
]
|
|
199
|
+
) # Obtains the perpendicular of the tip
|
|
173
200
|
tip.rotate(
|
|
174
201
|
-angles[2] + PI / 2,
|
|
175
202
|
axis=axis,
|
|
@@ -178,7 +205,7 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
178
205
|
tip.shift(anchor - tip.tip_point)
|
|
179
206
|
return tip
|
|
180
207
|
|
|
181
|
-
def reset_endpoints_based_on_tip(self, tip, at_start):
|
|
208
|
+
def reset_endpoints_based_on_tip(self, tip: tips.ArrowTip, at_start: bool) -> Self:
|
|
182
209
|
if self.get_length() == 0:
|
|
183
210
|
# Zero length, put_start_and_end_on wouldn't work
|
|
184
211
|
return self
|
|
@@ -189,7 +216,7 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
189
216
|
self.put_start_and_end_on(self.get_start(), tip.base)
|
|
190
217
|
return self
|
|
191
218
|
|
|
192
|
-
def asign_tip_attr(self, tip, at_start):
|
|
219
|
+
def asign_tip_attr(self, tip: tips.ArrowTip, at_start: bool) -> Self:
|
|
193
220
|
if at_start:
|
|
194
221
|
self.start_tip = tip
|
|
195
222
|
else:
|
|
@@ -198,15 +225,15 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
198
225
|
|
|
199
226
|
# Checking for tips
|
|
200
227
|
|
|
201
|
-
def has_tip(self):
|
|
228
|
+
def has_tip(self) -> bool:
|
|
202
229
|
return hasattr(self, "tip") and self.tip in self
|
|
203
230
|
|
|
204
|
-
def has_start_tip(self):
|
|
231
|
+
def has_start_tip(self) -> bool:
|
|
205
232
|
return hasattr(self, "start_tip") and self.start_tip in self
|
|
206
233
|
|
|
207
234
|
# Getters
|
|
208
235
|
|
|
209
|
-
def pop_tips(self):
|
|
236
|
+
def pop_tips(self) -> VGroup:
|
|
210
237
|
start, end = self.get_start_and_end()
|
|
211
238
|
result = self.get_group_class()()
|
|
212
239
|
if self.has_tip():
|
|
@@ -218,7 +245,7 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
218
245
|
self.put_start_and_end_on(start, end)
|
|
219
246
|
return result
|
|
220
247
|
|
|
221
|
-
def get_tips(self):
|
|
248
|
+
def get_tips(self) -> VGroup:
|
|
222
249
|
"""Returns a VGroup (collection of VMobjects) containing
|
|
223
250
|
the TipableVMObject instance's tips.
|
|
224
251
|
"""
|
|
@@ -229,39 +256,49 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|
|
229
256
|
result.add(self.start_tip)
|
|
230
257
|
return result
|
|
231
258
|
|
|
232
|
-
def get_tip(self):
|
|
259
|
+
def get_tip(self) -> VMobject:
|
|
233
260
|
"""Returns the TipableVMobject instance's (first) tip,
|
|
234
|
-
otherwise throws an exception.
|
|
261
|
+
otherwise throws an exception.
|
|
262
|
+
"""
|
|
235
263
|
tips = self.get_tips()
|
|
236
264
|
if len(tips) == 0:
|
|
237
265
|
raise Exception("tip not found")
|
|
238
266
|
else:
|
|
239
|
-
|
|
267
|
+
tip: VMobject = tips[0]
|
|
268
|
+
return tip
|
|
240
269
|
|
|
241
|
-
def get_default_tip_length(self):
|
|
270
|
+
def get_default_tip_length(self) -> float:
|
|
242
271
|
return self.tip_length
|
|
243
272
|
|
|
244
|
-
def get_first_handle(self):
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
273
|
+
def get_first_handle(self) -> Point3D:
|
|
274
|
+
# Type inference of extracting an element from a list, is not
|
|
275
|
+
# supported by numpy, see this numpy issue
|
|
276
|
+
# https://github.com/numpy/numpy/issues/16544
|
|
277
|
+
first_handle: Point3D = self.points[1]
|
|
278
|
+
return first_handle
|
|
279
|
+
|
|
280
|
+
def get_last_handle(self) -> Point3D:
|
|
281
|
+
# Type inference of extracting an element from a list, is not
|
|
282
|
+
# supported by numpy, see this numpy issue
|
|
283
|
+
# https://github.com/numpy/numpy/issues/16544
|
|
284
|
+
last_handle: Point3D = self.points[-2]
|
|
285
|
+
return last_handle
|
|
286
|
+
|
|
287
|
+
def get_end(self) -> Point3D:
|
|
251
288
|
if self.has_tip():
|
|
252
289
|
return self.tip.get_start()
|
|
253
290
|
else:
|
|
254
291
|
return super().get_end()
|
|
255
292
|
|
|
256
|
-
def get_start(self):
|
|
293
|
+
def get_start(self) -> Point3D:
|
|
257
294
|
if self.has_start_tip():
|
|
258
295
|
return self.start_tip.get_start()
|
|
259
296
|
else:
|
|
260
297
|
return super().get_start()
|
|
261
298
|
|
|
262
|
-
def get_length(self):
|
|
299
|
+
def get_length(self) -> float:
|
|
263
300
|
start, end = self.get_start_and_end()
|
|
264
|
-
return np.linalg.norm(start - end)
|
|
301
|
+
return float(np.linalg.norm(start - end))
|
|
265
302
|
|
|
266
303
|
|
|
267
304
|
class Arc(TipableVMobject):
|
|
@@ -281,24 +318,24 @@ class Arc(TipableVMobject):
|
|
|
281
318
|
|
|
282
319
|
def __init__(
|
|
283
320
|
self,
|
|
284
|
-
radius: float = 1.0,
|
|
285
|
-
start_angle=0,
|
|
286
|
-
angle=TAU / 4,
|
|
287
|
-
num_components=9,
|
|
288
|
-
arc_center=ORIGIN,
|
|
289
|
-
**kwargs,
|
|
321
|
+
radius: float | None = 1.0,
|
|
322
|
+
start_angle: float = 0,
|
|
323
|
+
angle: float = TAU / 4,
|
|
324
|
+
num_components: int = 9,
|
|
325
|
+
arc_center: Point3DLike = ORIGIN,
|
|
326
|
+
**kwargs: Any,
|
|
290
327
|
):
|
|
291
328
|
if radius is None: # apparently None is passed by ArcBetweenPoints
|
|
292
329
|
radius = 1.0
|
|
293
330
|
self.radius = radius
|
|
294
331
|
self.num_components = num_components
|
|
295
|
-
self.arc_center = arc_center
|
|
332
|
+
self.arc_center: Point3D = np.asarray(arc_center)
|
|
296
333
|
self.start_angle = start_angle
|
|
297
334
|
self.angle = angle
|
|
298
|
-
self._failed_to_get_center = False
|
|
335
|
+
self._failed_to_get_center: bool = False
|
|
299
336
|
super().__init__(**kwargs)
|
|
300
337
|
|
|
301
|
-
def generate_points(self):
|
|
338
|
+
def generate_points(self) -> None:
|
|
302
339
|
self._set_pre_positioned_points()
|
|
303
340
|
self.scale(self.radius, about_point=ORIGIN)
|
|
304
341
|
self.shift(self.arc_center)
|
|
@@ -306,7 +343,7 @@ class Arc(TipableVMobject):
|
|
|
306
343
|
# Points are set a bit differently when rendering via OpenGL.
|
|
307
344
|
# TODO: refactor Arc so that only one strategy for setting points
|
|
308
345
|
# has to be used.
|
|
309
|
-
def init_points(self):
|
|
346
|
+
def init_points(self) -> None:
|
|
310
347
|
self.set_points(
|
|
311
348
|
Arc._create_quadratic_bezier_points(
|
|
312
349
|
angle=self.angle,
|
|
@@ -318,7 +355,9 @@ class Arc(TipableVMobject):
|
|
|
318
355
|
self.shift(self.arc_center)
|
|
319
356
|
|
|
320
357
|
@staticmethod
|
|
321
|
-
def _create_quadratic_bezier_points(
|
|
358
|
+
def _create_quadratic_bezier_points(
|
|
359
|
+
angle: float, start_angle: float = 0, n_components: int = 8
|
|
360
|
+
) -> QuadraticSpline:
|
|
322
361
|
samples = np.array(
|
|
323
362
|
[
|
|
324
363
|
[np.cos(a), np.sin(a), 0]
|
|
@@ -338,7 +377,7 @@ class Arc(TipableVMobject):
|
|
|
338
377
|
points[2::3] = samples[2::2]
|
|
339
378
|
return points
|
|
340
379
|
|
|
341
|
-
def _set_pre_positioned_points(self):
|
|
380
|
+
def _set_pre_positioned_points(self) -> None:
|
|
342
381
|
anchors = np.array(
|
|
343
382
|
[
|
|
344
383
|
np.cos(a) * RIGHT + np.sin(a) * UP
|
|
@@ -357,11 +396,12 @@ class Arc(TipableVMobject):
|
|
|
357
396
|
tangent_vectors[:, 1] = anchors[:, 0]
|
|
358
397
|
tangent_vectors[:, 0] = -anchors[:, 1]
|
|
359
398
|
# Use tangent vectors to deduce anchors
|
|
360
|
-
|
|
361
|
-
|
|
399
|
+
factor = 4 / 3 * np.tan(d_theta / 4)
|
|
400
|
+
handles1 = anchors[:-1] + factor * tangent_vectors[:-1]
|
|
401
|
+
handles2 = anchors[1:] - factor * tangent_vectors[1:]
|
|
362
402
|
self.set_anchors_and_handles(anchors[:-1], handles1, handles2, anchors[1:])
|
|
363
403
|
|
|
364
|
-
def get_arc_center(self, warning=True):
|
|
404
|
+
def get_arc_center(self, warning: bool = True) -> Point3D:
|
|
365
405
|
"""Looks at the normals to the first two
|
|
366
406
|
anchors, and finds their intersection points
|
|
367
407
|
"""
|
|
@@ -372,7 +412,7 @@ class Arc(TipableVMobject):
|
|
|
372
412
|
# For a1 and a2 to lie at the same point arc radius
|
|
373
413
|
# must be zero. Thus arc_center will also lie at
|
|
374
414
|
# that point.
|
|
375
|
-
return a1
|
|
415
|
+
return np.copy(a1)
|
|
376
416
|
# Tangent vectors
|
|
377
417
|
t1 = h1 - a1
|
|
378
418
|
t2 = h2 - a2
|
|
@@ -383,16 +423,21 @@ class Arc(TipableVMobject):
|
|
|
383
423
|
return line_intersection(line1=(a1, a1 + n1), line2=(a2, a2 + n2))
|
|
384
424
|
except Exception:
|
|
385
425
|
if warning:
|
|
386
|
-
warnings.warn(
|
|
426
|
+
warnings.warn(
|
|
427
|
+
"Can't find Arc center, using ORIGIN instead", stacklevel=1
|
|
428
|
+
)
|
|
387
429
|
self._failed_to_get_center = True
|
|
388
430
|
return np.array(ORIGIN)
|
|
389
431
|
|
|
390
|
-
def move_arc_center_to(self, point):
|
|
432
|
+
def move_arc_center_to(self, point: Point3DLike) -> Self:
|
|
391
433
|
self.shift(point - self.get_arc_center())
|
|
392
434
|
return self
|
|
393
435
|
|
|
394
|
-
def stop_angle(self):
|
|
395
|
-
return
|
|
436
|
+
def stop_angle(self) -> float:
|
|
437
|
+
return cast(
|
|
438
|
+
float,
|
|
439
|
+
angle_of_vector(self.points[-1] - self.get_arc_center()) % TAU,
|
|
440
|
+
)
|
|
396
441
|
|
|
397
442
|
|
|
398
443
|
class ArcBetweenPoints(Arc):
|
|
@@ -414,7 +459,14 @@ class ArcBetweenPoints(Arc):
|
|
|
414
459
|
self.play(Create(arc))
|
|
415
460
|
"""
|
|
416
461
|
|
|
417
|
-
def __init__(
|
|
462
|
+
def __init__(
|
|
463
|
+
self,
|
|
464
|
+
start: Point3DLike,
|
|
465
|
+
end: Point3DLike,
|
|
466
|
+
angle: float = TAU / 4,
|
|
467
|
+
radius: float | None = None,
|
|
468
|
+
**kwargs: Any,
|
|
469
|
+
) -> None:
|
|
418
470
|
if radius is not None:
|
|
419
471
|
self.radius = radius
|
|
420
472
|
if radius < 0:
|
|
@@ -428,24 +480,94 @@ class ArcBetweenPoints(Arc):
|
|
|
428
480
|
"""ArcBetweenPoints called with a radius that is
|
|
429
481
|
smaller than half the distance between the points.""",
|
|
430
482
|
)
|
|
431
|
-
arc_height = radius -
|
|
432
|
-
angle =
|
|
483
|
+
arc_height = radius - np.sqrt(radius**2 - halfdist**2)
|
|
484
|
+
angle = np.arccos((radius - arc_height) / radius) * sign
|
|
433
485
|
|
|
434
486
|
super().__init__(radius=radius, angle=angle, **kwargs)
|
|
435
487
|
if angle == 0:
|
|
436
|
-
self.set_points_as_corners([LEFT, RIGHT])
|
|
488
|
+
self.set_points_as_corners(np.array([LEFT, RIGHT]))
|
|
437
489
|
self.put_start_and_end_on(start, end)
|
|
438
490
|
|
|
439
491
|
if radius is None:
|
|
440
492
|
center = self.get_arc_center(warning=False)
|
|
441
493
|
if not self._failed_to_get_center:
|
|
442
|
-
|
|
494
|
+
# np.linalg.norm returns floating[Any] which is not compatible with float
|
|
495
|
+
self.radius = cast(
|
|
496
|
+
float, np.linalg.norm(np.array(start) - np.array(center))
|
|
497
|
+
)
|
|
443
498
|
else:
|
|
444
|
-
self.radius =
|
|
499
|
+
self.radius = np.inf
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class TangentialArc(ArcBetweenPoints):
|
|
503
|
+
"""
|
|
504
|
+
Construct an arc that is tangent to two intersecting lines.
|
|
505
|
+
You can choose any of the 4 possible corner arcs via the `corner` tuple.
|
|
506
|
+
corner = (s1, s2) where each si is ±1 to control direction along each line.
|
|
507
|
+
|
|
508
|
+
Example
|
|
509
|
+
-------
|
|
510
|
+
.. manim:: TangentialArcExample
|
|
511
|
+
|
|
512
|
+
class TangentialArcExample(Scene):
|
|
513
|
+
def construct(self):
|
|
514
|
+
line1 = DashedLine(start=3 * LEFT, end=3 * RIGHT)
|
|
515
|
+
line1.rotate(angle=31 * DEGREES, about_point=ORIGIN)
|
|
516
|
+
line2 = DashedLine(start=3 * UP, end=3 * DOWN)
|
|
517
|
+
line2.rotate(angle=12 * DEGREES, about_point=ORIGIN)
|
|
518
|
+
|
|
519
|
+
arc = TangentialArc(line1, line2, radius=2.25, corner=(1, 1), color=TEAL)
|
|
520
|
+
self.add(arc, line1, line2)
|
|
521
|
+
"""
|
|
522
|
+
|
|
523
|
+
def __init__(
|
|
524
|
+
self,
|
|
525
|
+
line1: Line,
|
|
526
|
+
line2: Line,
|
|
527
|
+
radius: float,
|
|
528
|
+
corner: Any = (1, 1),
|
|
529
|
+
**kwargs: Any,
|
|
530
|
+
):
|
|
531
|
+
self.line1 = line1
|
|
532
|
+
self.line2 = line2
|
|
533
|
+
|
|
534
|
+
intersection_point = line_intersection(
|
|
535
|
+
[line1.get_start(), line1.get_end()], [line2.get_start(), line2.get_end()]
|
|
536
|
+
)
|
|
537
|
+
|
|
538
|
+
s1, s2 = corner
|
|
539
|
+
# Get unit vector for specified directions
|
|
540
|
+
unit_vector1 = s1 * line1.get_unit_vector()
|
|
541
|
+
unit_vector2 = s2 * line2.get_unit_vector()
|
|
542
|
+
|
|
543
|
+
corner_angle = angle_between_vectors(unit_vector1, unit_vector2)
|
|
544
|
+
tangent_point_distance = radius / np.tan(corner_angle / 2)
|
|
545
|
+
|
|
546
|
+
# tangent points
|
|
547
|
+
tangent_point1 = intersection_point + tangent_point_distance * unit_vector1
|
|
548
|
+
tangent_point2 = intersection_point + tangent_point_distance * unit_vector2
|
|
549
|
+
|
|
550
|
+
cross_product = (
|
|
551
|
+
unit_vector1[0] * unit_vector2[1] - unit_vector1[1] * unit_vector2[0]
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
# Determine start and end points based on orientation
|
|
555
|
+
if cross_product < 0:
|
|
556
|
+
# Counterclockwise orientation - standard order
|
|
557
|
+
start_point = tangent_point1
|
|
558
|
+
end_point = tangent_point2
|
|
559
|
+
else:
|
|
560
|
+
# Clockwise orientation - reverse the points
|
|
561
|
+
start_point = tangent_point2
|
|
562
|
+
end_point = tangent_point1
|
|
563
|
+
|
|
564
|
+
super().__init__(start=start_point, end=end_point, radius=radius, **kwargs)
|
|
445
565
|
|
|
446
566
|
|
|
447
567
|
class CurvedArrow(ArcBetweenPoints):
|
|
448
|
-
def __init__(
|
|
568
|
+
def __init__(
|
|
569
|
+
self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any
|
|
570
|
+
) -> None:
|
|
449
571
|
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
|
|
450
572
|
|
|
451
573
|
tip_shape = kwargs.pop("tip_shape", ArrowTriangleFilledTip)
|
|
@@ -454,7 +576,9 @@ class CurvedArrow(ArcBetweenPoints):
|
|
|
454
576
|
|
|
455
577
|
|
|
456
578
|
class CurvedDoubleArrow(CurvedArrow):
|
|
457
|
-
def __init__(
|
|
579
|
+
def __init__(
|
|
580
|
+
self, start_point: Point3DLike, end_point: Point3DLike, **kwargs: Any
|
|
581
|
+
) -> None:
|
|
458
582
|
if "tip_shape_end" in kwargs:
|
|
459
583
|
kwargs["tip_shape"] = kwargs.pop("tip_shape_end")
|
|
460
584
|
from manim.mobject.geometry.tips import ArrowTriangleFilledTip
|
|
@@ -492,9 +616,9 @@ class Circle(Arc):
|
|
|
492
616
|
def __init__(
|
|
493
617
|
self,
|
|
494
618
|
radius: float | None = None,
|
|
495
|
-
color:
|
|
496
|
-
**kwargs,
|
|
497
|
-
):
|
|
619
|
+
color: ParsableManimColor = RED,
|
|
620
|
+
**kwargs: Any,
|
|
621
|
+
) -> None:
|
|
498
622
|
super().__init__(
|
|
499
623
|
radius=radius,
|
|
500
624
|
start_angle=0,
|
|
@@ -509,7 +633,7 @@ class Circle(Arc):
|
|
|
509
633
|
dim_to_match: int = 0,
|
|
510
634
|
stretch: bool = False,
|
|
511
635
|
buffer_factor: float = 1.2,
|
|
512
|
-
):
|
|
636
|
+
) -> Self:
|
|
513
637
|
"""Modifies a circle so that it surrounds a given mobject.
|
|
514
638
|
|
|
515
639
|
Parameters
|
|
@@ -545,7 +669,6 @@ class Circle(Arc):
|
|
|
545
669
|
group = Group(group1, group2, group3).arrange(buff=1)
|
|
546
670
|
self.add(group)
|
|
547
671
|
"""
|
|
548
|
-
|
|
549
672
|
# Ignores dim_to_match and stretch; result will always be a circle
|
|
550
673
|
# TODO: Perhaps create an ellipse class to handle single-dimension stretching
|
|
551
674
|
|
|
@@ -556,7 +679,7 @@ class Circle(Arc):
|
|
|
556
679
|
self.width = np.sqrt(mobject.width**2 + mobject.height**2)
|
|
557
680
|
return self.scale(buffer_factor)
|
|
558
681
|
|
|
559
|
-
def point_at_angle(self, angle: float):
|
|
682
|
+
def point_at_angle(self, angle: float) -> Point3D:
|
|
560
683
|
"""Returns the position of a point on the circle.
|
|
561
684
|
|
|
562
685
|
Parameters
|
|
@@ -585,16 +708,15 @@ class Circle(Arc):
|
|
|
585
708
|
self.add(circle, s1, s2)
|
|
586
709
|
|
|
587
710
|
"""
|
|
588
|
-
|
|
589
711
|
start_angle = angle_of_vector(self.points[0] - self.get_center())
|
|
590
712
|
proportion = (angle - start_angle) / TAU
|
|
591
|
-
proportion -=
|
|
713
|
+
proportion -= np.floor(proportion)
|
|
592
714
|
return self.point_from_proportion(proportion)
|
|
593
715
|
|
|
594
716
|
@staticmethod
|
|
595
717
|
def from_three_points(
|
|
596
|
-
p1:
|
|
597
|
-
):
|
|
718
|
+
p1: Point3DLike, p2: Point3DLike, p3: Point3DLike, **kwargs: Any
|
|
719
|
+
) -> Circle:
|
|
598
720
|
"""Returns a circle passing through the specified
|
|
599
721
|
three points.
|
|
600
722
|
|
|
@@ -614,10 +736,11 @@ class Circle(Arc):
|
|
|
614
736
|
self.add(NumberPlane(), circle, dots)
|
|
615
737
|
"""
|
|
616
738
|
center = line_intersection(
|
|
617
|
-
perpendicular_bisector([p1, p2]),
|
|
618
|
-
perpendicular_bisector([p2, p3]),
|
|
739
|
+
perpendicular_bisector([np.asarray(p1), np.asarray(p2)]),
|
|
740
|
+
perpendicular_bisector([np.asarray(p2), np.asarray(p3)]),
|
|
619
741
|
)
|
|
620
|
-
|
|
742
|
+
# np.linalg.norm returns floating[Any] which is not compatible with float
|
|
743
|
+
radius = cast(float, np.linalg.norm(p1 - center))
|
|
621
744
|
return Circle(radius=radius, **kwargs).shift(center)
|
|
622
745
|
|
|
623
746
|
|
|
@@ -654,13 +777,13 @@ class Dot(Circle):
|
|
|
654
777
|
|
|
655
778
|
def __init__(
|
|
656
779
|
self,
|
|
657
|
-
point:
|
|
780
|
+
point: Point3DLike = ORIGIN,
|
|
658
781
|
radius: float = DEFAULT_DOT_RADIUS,
|
|
659
782
|
stroke_width: float = 0,
|
|
660
783
|
fill_opacity: float = 1.0,
|
|
661
|
-
color:
|
|
662
|
-
**kwargs,
|
|
663
|
-
):
|
|
784
|
+
color: ParsableManimColor = WHITE,
|
|
785
|
+
**kwargs: Any,
|
|
786
|
+
) -> None:
|
|
664
787
|
super().__init__(
|
|
665
788
|
arc_center=point,
|
|
666
789
|
radius=radius,
|
|
@@ -677,11 +800,11 @@ class AnnotationDot(Dot):
|
|
|
677
800
|
def __init__(
|
|
678
801
|
self,
|
|
679
802
|
radius: float = DEFAULT_DOT_RADIUS * 1.3,
|
|
680
|
-
stroke_width=5,
|
|
681
|
-
stroke_color=WHITE,
|
|
682
|
-
fill_color=BLUE,
|
|
683
|
-
**kwargs,
|
|
684
|
-
):
|
|
803
|
+
stroke_width: float = 5,
|
|
804
|
+
stroke_color: ParsableManimColor = WHITE,
|
|
805
|
+
fill_color: ParsableManimColor = BLUE,
|
|
806
|
+
**kwargs: Any,
|
|
807
|
+
) -> None:
|
|
685
808
|
super().__init__(
|
|
686
809
|
radius=radius,
|
|
687
810
|
stroke_width=stroke_width,
|
|
@@ -702,8 +825,9 @@ class LabeledDot(Dot):
|
|
|
702
825
|
representing rendered strings like :class:`~.Text` or :class:`~.Tex`
|
|
703
826
|
can be passed as well.
|
|
704
827
|
radius
|
|
705
|
-
The radius of the :class:`Dot`. If
|
|
706
|
-
is calculated based on the size
|
|
828
|
+
The radius of the :class:`Dot`. If provided, the ``buff`` is ignored.
|
|
829
|
+
If ``None`` (the default), the radius is calculated based on the size
|
|
830
|
+
of the ``label`` and the ``buff``.
|
|
707
831
|
|
|
708
832
|
Examples
|
|
709
833
|
--------
|
|
@@ -729,17 +853,20 @@ class LabeledDot(Dot):
|
|
|
729
853
|
self,
|
|
730
854
|
label: str | SingleStringMathTex | Text | Tex,
|
|
731
855
|
radius: float | None = None,
|
|
732
|
-
|
|
856
|
+
buff: float = SMALL_BUFF,
|
|
857
|
+
**kwargs: Any,
|
|
733
858
|
) -> None:
|
|
734
859
|
if isinstance(label, str):
|
|
735
860
|
from manim import MathTex
|
|
736
861
|
|
|
737
|
-
rendered_label = MathTex(label, color=BLACK)
|
|
862
|
+
rendered_label: VMobject = MathTex(label, color=BLACK)
|
|
738
863
|
else:
|
|
739
864
|
rendered_label = label
|
|
740
865
|
|
|
741
866
|
if radius is None:
|
|
742
|
-
radius =
|
|
867
|
+
radius = buff + float(
|
|
868
|
+
np.linalg.norm([rendered_label.width, rendered_label.height]) / 2
|
|
869
|
+
)
|
|
743
870
|
super().__init__(radius=radius, **kwargs)
|
|
744
871
|
rendered_label.move_to(self.get_center())
|
|
745
872
|
self.add(rendered_label)
|
|
@@ -770,14 +897,16 @@ class Ellipse(Circle):
|
|
|
770
897
|
self.add(ellipse_group)
|
|
771
898
|
"""
|
|
772
899
|
|
|
773
|
-
def __init__(self, width: float = 2, height: float = 1, **kwargs):
|
|
900
|
+
def __init__(self, width: float = 2, height: float = 1, **kwargs: Any) -> None:
|
|
774
901
|
super().__init__(**kwargs)
|
|
775
902
|
self.stretch_to_fit_width(width)
|
|
776
903
|
self.stretch_to_fit_height(height)
|
|
777
904
|
|
|
778
905
|
|
|
779
906
|
class AnnularSector(Arc):
|
|
780
|
-
"""
|
|
907
|
+
"""A sector of an annulus.
|
|
908
|
+
|
|
909
|
+
|
|
781
910
|
Parameters
|
|
782
911
|
----------
|
|
783
912
|
inner_radius
|
|
@@ -822,15 +951,15 @@ class AnnularSector(Arc):
|
|
|
822
951
|
|
|
823
952
|
def __init__(
|
|
824
953
|
self,
|
|
825
|
-
inner_radius=1,
|
|
826
|
-
outer_radius=2,
|
|
827
|
-
angle=TAU / 4,
|
|
828
|
-
start_angle=0,
|
|
829
|
-
fill_opacity=1,
|
|
830
|
-
stroke_width=0,
|
|
831
|
-
color=WHITE,
|
|
832
|
-
**kwargs,
|
|
833
|
-
):
|
|
954
|
+
inner_radius: float = 1,
|
|
955
|
+
outer_radius: float = 2,
|
|
956
|
+
angle: float = TAU / 4,
|
|
957
|
+
start_angle: float = 0,
|
|
958
|
+
fill_opacity: float = 1,
|
|
959
|
+
stroke_width: float = 0,
|
|
960
|
+
color: ParsableManimColor = WHITE,
|
|
961
|
+
**kwargs: Any,
|
|
962
|
+
) -> None:
|
|
834
963
|
self.inner_radius = inner_radius
|
|
835
964
|
self.outer_radius = outer_radius
|
|
836
965
|
super().__init__(
|
|
@@ -842,7 +971,7 @@ class AnnularSector(Arc):
|
|
|
842
971
|
**kwargs,
|
|
843
972
|
)
|
|
844
973
|
|
|
845
|
-
def generate_points(self):
|
|
974
|
+
def generate_points(self) -> None:
|
|
846
975
|
inner_arc, outer_arc = (
|
|
847
976
|
Arc(
|
|
848
977
|
start_angle=self.start_angle,
|
|
@@ -858,11 +987,13 @@ class AnnularSector(Arc):
|
|
|
858
987
|
self.append_points(outer_arc.points)
|
|
859
988
|
self.add_line_to(inner_arc.points[0])
|
|
860
989
|
|
|
861
|
-
init_points
|
|
990
|
+
def init_points(self) -> None:
|
|
991
|
+
self.generate_points()
|
|
862
992
|
|
|
863
993
|
|
|
864
994
|
class Sector(AnnularSector):
|
|
865
|
-
"""
|
|
995
|
+
"""A sector of a circle.
|
|
996
|
+
|
|
866
997
|
Examples
|
|
867
998
|
--------
|
|
868
999
|
.. manim:: ExampleSector
|
|
@@ -870,15 +1001,15 @@ class Sector(AnnularSector):
|
|
|
870
1001
|
|
|
871
1002
|
class ExampleSector(Scene):
|
|
872
1003
|
def construct(self):
|
|
873
|
-
sector = Sector(
|
|
874
|
-
sector2 = Sector(
|
|
1004
|
+
sector = Sector(radius=2)
|
|
1005
|
+
sector2 = Sector(radius=2.5, angle=60*DEGREES).move_to([-3, 0, 0])
|
|
875
1006
|
sector.set_color(RED)
|
|
876
1007
|
sector2.set_color(PINK)
|
|
877
1008
|
self.add(sector, sector2)
|
|
878
1009
|
"""
|
|
879
1010
|
|
|
880
|
-
def __init__(self,
|
|
881
|
-
super().__init__(inner_radius=
|
|
1011
|
+
def __init__(self, radius: float = 1, **kwargs: Any) -> None:
|
|
1012
|
+
super().__init__(inner_radius=0, outer_radius=radius, **kwargs)
|
|
882
1013
|
|
|
883
1014
|
|
|
884
1015
|
class Annulus(Circle):
|
|
@@ -907,14 +1038,14 @@ class Annulus(Circle):
|
|
|
907
1038
|
|
|
908
1039
|
def __init__(
|
|
909
1040
|
self,
|
|
910
|
-
inner_radius: float
|
|
911
|
-
outer_radius: float
|
|
912
|
-
fill_opacity=1,
|
|
913
|
-
stroke_width=0,
|
|
914
|
-
color=WHITE,
|
|
915
|
-
mark_paths_closed=False,
|
|
916
|
-
**kwargs,
|
|
917
|
-
):
|
|
1041
|
+
inner_radius: float = 1,
|
|
1042
|
+
outer_radius: float = 2,
|
|
1043
|
+
fill_opacity: float = 1,
|
|
1044
|
+
stroke_width: float = 0,
|
|
1045
|
+
color: ParsableManimColor = WHITE,
|
|
1046
|
+
mark_paths_closed: bool = False,
|
|
1047
|
+
**kwargs: Any,
|
|
1048
|
+
) -> None:
|
|
918
1049
|
self.mark_paths_closed = mark_paths_closed # is this even used?
|
|
919
1050
|
self.inner_radius = inner_radius
|
|
920
1051
|
self.outer_radius = outer_radius
|
|
@@ -922,7 +1053,7 @@ class Annulus(Circle):
|
|
|
922
1053
|
fill_opacity=fill_opacity, stroke_width=stroke_width, color=color, **kwargs
|
|
923
1054
|
)
|
|
924
1055
|
|
|
925
|
-
def generate_points(self):
|
|
1056
|
+
def generate_points(self) -> None:
|
|
926
1057
|
self.radius = self.outer_radius
|
|
927
1058
|
outer_circle = Circle(radius=self.outer_radius)
|
|
928
1059
|
inner_circle = Circle(radius=self.inner_radius)
|
|
@@ -931,11 +1062,13 @@ class Annulus(Circle):
|
|
|
931
1062
|
self.append_points(inner_circle.points)
|
|
932
1063
|
self.shift(self.arc_center)
|
|
933
1064
|
|
|
934
|
-
init_points
|
|
1065
|
+
def init_points(self) -> None:
|
|
1066
|
+
self.generate_points()
|
|
935
1067
|
|
|
936
1068
|
|
|
937
1069
|
class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
|
|
938
|
-
"""
|
|
1070
|
+
"""A cubic Bézier curve.
|
|
1071
|
+
|
|
939
1072
|
Example
|
|
940
1073
|
-------
|
|
941
1074
|
.. manim:: BezierSplineExample
|
|
@@ -956,7 +1089,14 @@ class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
|
|
|
956
1089
|
|
|
957
1090
|
"""
|
|
958
1091
|
|
|
959
|
-
def __init__(
|
|
1092
|
+
def __init__(
|
|
1093
|
+
self,
|
|
1094
|
+
start_anchor: Point3DLike,
|
|
1095
|
+
start_handle: Point3DLike,
|
|
1096
|
+
end_handle: Point3DLike,
|
|
1097
|
+
end_anchor: Point3DLike,
|
|
1098
|
+
**kwargs: Any,
|
|
1099
|
+
) -> None:
|
|
960
1100
|
super().__init__(**kwargs)
|
|
961
1101
|
self.add_cubic_bezier_curve(start_anchor, start_handle, end_handle, end_anchor)
|
|
962
1102
|
|
|
@@ -1042,18 +1182,20 @@ class ArcPolygon(VMobject, metaclass=ConvertToOpenGL):
|
|
|
1042
1182
|
|
|
1043
1183
|
def __init__(
|
|
1044
1184
|
self,
|
|
1045
|
-
*vertices:
|
|
1185
|
+
*vertices: Point3DLike,
|
|
1046
1186
|
angle: float = PI / 4,
|
|
1047
1187
|
radius: float | None = None,
|
|
1048
1188
|
arc_config: list[dict] | None = None,
|
|
1049
|
-
**kwargs,
|
|
1050
|
-
):
|
|
1189
|
+
**kwargs: Any,
|
|
1190
|
+
) -> None:
|
|
1051
1191
|
n = len(vertices)
|
|
1052
1192
|
point_pairs = [(vertices[k], vertices[(k + 1) % n]) for k in range(n)]
|
|
1053
1193
|
|
|
1054
1194
|
if not arc_config:
|
|
1055
1195
|
if radius:
|
|
1056
|
-
all_arc_configs = itertools.repeat(
|
|
1196
|
+
all_arc_configs: Iterable[dict] = itertools.repeat(
|
|
1197
|
+
{"radius": radius}, len(point_pairs)
|
|
1198
|
+
)
|
|
1057
1199
|
else:
|
|
1058
1200
|
all_arc_configs = itertools.repeat({"angle": angle}, len(point_pairs))
|
|
1059
1201
|
elif isinstance(arc_config, dict):
|
|
@@ -1185,7 +1327,7 @@ class ArcPolygonFromArcs(VMobject, metaclass=ConvertToOpenGL):
|
|
|
1185
1327
|
self.wait(2)
|
|
1186
1328
|
"""
|
|
1187
1329
|
|
|
1188
|
-
def __init__(self, *arcs: Arc | ArcBetweenPoints, **kwargs):
|
|
1330
|
+
def __init__(self, *arcs: Arc | ArcBetweenPoints, **kwargs: Any) -> None:
|
|
1189
1331
|
if not all(isinstance(m, (Arc, ArcBetweenPoints)) for m in arcs):
|
|
1190
1332
|
raise ValueError(
|
|
1191
1333
|
"All ArcPolygon submobjects must be of type Arc/ArcBetweenPoints",
|
|
@@ -1204,7 +1346,7 @@ class ArcPolygonFromArcs(VMobject, metaclass=ConvertToOpenGL):
|
|
|
1204
1346
|
self.append_points(arc1.points)
|
|
1205
1347
|
line = Line(arc1.get_end(), arc2.get_start())
|
|
1206
1348
|
len_ratio = line.get_length() / arc1.get_arc_length()
|
|
1207
|
-
if
|
|
1349
|
+
if np.isnan(len_ratio) or np.isinf(len_ratio):
|
|
1208
1350
|
continue
|
|
1209
1351
|
line.insert_n_curves(int(arc1.get_num_curves() * len_ratio))
|
|
1210
1352
|
self.append_points(line.points)
|