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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
from pathops import Path as SkiaPath
|
|
@@ -12,6 +12,9 @@ from manim import config
|
|
|
12
12
|
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
13
13
|
from manim.mobject.types.vectorized_mobject import VMobject
|
|
14
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from manim.typing import Point2DLike_Array, Point3D_Array, Point3DLike_Array
|
|
17
|
+
|
|
15
18
|
from ...constants import RendererType
|
|
16
19
|
|
|
17
20
|
__all__ = ["Union", "Intersection", "Difference", "Exclusion"]
|
|
@@ -23,41 +26,39 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
23
26
|
objects (:class:`~.VMobject`).
|
|
24
27
|
"""
|
|
25
28
|
|
|
26
|
-
def __init__(self, *args, **kwargs):
|
|
27
|
-
super().__init__(*args, **kwargs)
|
|
28
|
-
|
|
29
29
|
def _convert_2d_to_3d_array(
|
|
30
30
|
self,
|
|
31
|
-
points:
|
|
31
|
+
points: Point2DLike_Array | Point3DLike_Array,
|
|
32
32
|
z_dim: float = 0.0,
|
|
33
|
-
) ->
|
|
34
|
-
"""Converts an iterable with coordinates in
|
|
35
|
-
:attr:`z_dim` as the
|
|
33
|
+
) -> Point3D_Array:
|
|
34
|
+
"""Converts an iterable with coordinates in 2D to 3D by adding
|
|
35
|
+
:attr:`z_dim` as the Z coordinate.
|
|
36
36
|
|
|
37
37
|
Parameters
|
|
38
38
|
----------
|
|
39
|
-
points
|
|
40
|
-
An iterable
|
|
41
|
-
z_dim
|
|
42
|
-
|
|
39
|
+
points
|
|
40
|
+
An iterable of points.
|
|
41
|
+
z_dim
|
|
42
|
+
Default value for the Z coordinate.
|
|
43
43
|
|
|
44
44
|
Returns
|
|
45
45
|
-------
|
|
46
|
-
|
|
47
|
-
A list of
|
|
46
|
+
Point3D_Array
|
|
47
|
+
A list of the points converted to 3D.
|
|
48
48
|
|
|
49
49
|
Example
|
|
50
50
|
-------
|
|
51
51
|
>>> a = _BooleanOps()
|
|
52
52
|
>>> p = [(1, 2), (3, 4)]
|
|
53
53
|
>>> a._convert_2d_to_3d_array(p)
|
|
54
|
-
|
|
54
|
+
array([[1., 2., 0.],
|
|
55
|
+
[3., 4., 0.]])
|
|
55
56
|
"""
|
|
56
|
-
|
|
57
|
-
for i, point in enumerate(
|
|
57
|
+
list_of_points = list(points)
|
|
58
|
+
for i, point in enumerate(list_of_points):
|
|
58
59
|
if len(point) == 2:
|
|
59
|
-
|
|
60
|
-
return
|
|
60
|
+
list_of_points[i] = np.append(point, z_dim)
|
|
61
|
+
return np.asarray(list_of_points)
|
|
61
62
|
|
|
62
63
|
def _convert_vmobject_to_skia_path(self, vmobject: VMobject) -> SkiaPath:
|
|
63
64
|
"""Converts a :class:`~.VMobject` to SkiaPath. This method only works for
|
|
@@ -70,15 +71,15 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
70
71
|
|
|
71
72
|
Returns
|
|
72
73
|
-------
|
|
73
|
-
SkiaPath
|
|
74
|
+
SkiaPath
|
|
74
75
|
The converted path.
|
|
75
76
|
"""
|
|
76
77
|
path = SkiaPath()
|
|
77
78
|
|
|
78
|
-
if
|
|
79
|
-
points = np.zeros((1, 3)) # point invalid?
|
|
80
|
-
else:
|
|
79
|
+
if np.all(np.isfinite(vmobject.points)):
|
|
81
80
|
points = vmobject.points
|
|
81
|
+
else:
|
|
82
|
+
points = np.zeros((1, 3)) # point invalid?
|
|
82
83
|
|
|
83
84
|
if len(points) == 0: # what? No points so return empty path
|
|
84
85
|
return path
|
|
@@ -90,17 +91,17 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
90
91
|
quads = vmobject.get_bezier_tuples_from_points(subpath)
|
|
91
92
|
start = subpath[0]
|
|
92
93
|
path.moveTo(*start[:2])
|
|
93
|
-
for
|
|
94
|
+
for _p0, p1, p2 in quads:
|
|
94
95
|
path.quadTo(*p1[:2], *p2[:2])
|
|
95
96
|
if vmobject.consider_points_equals(subpath[0], subpath[-1]):
|
|
96
97
|
path.close()
|
|
97
98
|
elif config.renderer == RendererType.CAIRO:
|
|
98
|
-
subpaths = vmobject.gen_subpaths_from_points_2d(points)
|
|
99
|
+
subpaths = vmobject.gen_subpaths_from_points_2d(points) # type: ignore[assignment]
|
|
99
100
|
for subpath in subpaths:
|
|
100
101
|
quads = vmobject.gen_cubic_bezier_tuples_from_points(subpath)
|
|
101
102
|
start = subpath[0]
|
|
102
103
|
path.moveTo(*start[:2])
|
|
103
|
-
for
|
|
104
|
+
for _p0, p1, p2, p3 in quads:
|
|
104
105
|
path.cubicTo(*p1[:2], *p2[:2], *p3[:2])
|
|
105
106
|
|
|
106
107
|
if vmobject.consider_points_equals_2d(subpath[0], subpath[-1]):
|
|
@@ -142,7 +143,7 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
142
143
|
n1, n2 = self._convert_2d_to_3d_array(points)
|
|
143
144
|
vmobject.add_quadratic_bezier_curve_to(n1, n2)
|
|
144
145
|
else:
|
|
145
|
-
raise Exception("Unsupported:
|
|
146
|
+
raise Exception(f"Unsupported: {path_verb}")
|
|
146
147
|
return vmobject
|
|
147
148
|
|
|
148
149
|
|
|
@@ -177,7 +178,7 @@ class Union(_BooleanOps):
|
|
|
177
178
|
|
|
178
179
|
"""
|
|
179
180
|
|
|
180
|
-
def __init__(self, *vmobjects: VMobject, **kwargs) -> None:
|
|
181
|
+
def __init__(self, *vmobjects: VMobject, **kwargs: Any) -> None:
|
|
181
182
|
if len(vmobjects) < 2:
|
|
182
183
|
raise ValueError("At least 2 mobjects needed for Union.")
|
|
183
184
|
super().__init__(**kwargs)
|
|
@@ -216,7 +217,7 @@ class Difference(_BooleanOps):
|
|
|
216
217
|
|
|
217
218
|
"""
|
|
218
219
|
|
|
219
|
-
def __init__(self, subject, clip, **kwargs) -> None:
|
|
220
|
+
def __init__(self, subject: VMobject, clip: VMobject, **kwargs: Any) -> None:
|
|
220
221
|
super().__init__(**kwargs)
|
|
221
222
|
outpen = SkiaPath()
|
|
222
223
|
difference(
|
|
@@ -258,7 +259,7 @@ class Intersection(_BooleanOps):
|
|
|
258
259
|
|
|
259
260
|
"""
|
|
260
261
|
|
|
261
|
-
def __init__(self, *vmobjects, **kwargs) -> None:
|
|
262
|
+
def __init__(self, *vmobjects: VMobject, **kwargs: Any) -> None:
|
|
262
263
|
if len(vmobjects) < 2:
|
|
263
264
|
raise ValueError("At least 2 mobjects needed for Intersection.")
|
|
264
265
|
|
|
@@ -311,7 +312,7 @@ class Exclusion(_BooleanOps):
|
|
|
311
312
|
|
|
312
313
|
"""
|
|
313
314
|
|
|
314
|
-
def __init__(self, subject, clip, **kwargs) -> None:
|
|
315
|
+
def __init__(self, subject: VMobject, clip: VMobject, **kwargs: Any) -> None:
|
|
315
316
|
super().__init__(**kwargs)
|
|
316
317
|
outpen = SkiaPath()
|
|
317
318
|
xor(
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
r"""Mobjects that inherit from lines and contain a label along the length."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = ["Label", "LabeledLine", "LabeledArrow", "LabeledPolygram"]
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from manim.constants import *
|
|
12
|
+
from manim.mobject.geometry.line import Arrow, Line
|
|
13
|
+
from manim.mobject.geometry.polygram import Polygram
|
|
14
|
+
from manim.mobject.geometry.shape_matchers import (
|
|
15
|
+
BackgroundRectangle,
|
|
16
|
+
SurroundingRectangle,
|
|
17
|
+
)
|
|
18
|
+
from manim.mobject.text.tex_mobject import MathTex, Tex
|
|
19
|
+
from manim.mobject.text.text_mobject import Text
|
|
20
|
+
from manim.mobject.types.vectorized_mobject import VGroup
|
|
21
|
+
from manim.utils.color import WHITE
|
|
22
|
+
from manim.utils.polylabel import polylabel
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from manim.typing import Point3DLike_Array
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Label(VGroup):
|
|
29
|
+
"""A Label consisting of text surrounded by a frame.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
label
|
|
34
|
+
Label that will be displayed.
|
|
35
|
+
label_config
|
|
36
|
+
A dictionary containing the configuration for the label.
|
|
37
|
+
This is only applied if ``label`` is of type ``str``.
|
|
38
|
+
box_config
|
|
39
|
+
A dictionary containing the configuration for the background box.
|
|
40
|
+
frame_config
|
|
41
|
+
A dictionary containing the configuration for the frame.
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
.. manim:: LabelExample
|
|
46
|
+
:save_last_frame:
|
|
47
|
+
:quality: high
|
|
48
|
+
|
|
49
|
+
class LabelExample(Scene):
|
|
50
|
+
def construct(self):
|
|
51
|
+
label = Label(
|
|
52
|
+
label=Text('Label Text', font='sans-serif'),
|
|
53
|
+
box_config = {
|
|
54
|
+
"color" : BLUE,
|
|
55
|
+
"fill_opacity" : 0.75
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
label.scale(3)
|
|
59
|
+
self.add(label)
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
label: str | Tex | MathTex | Text,
|
|
65
|
+
label_config: dict[str, Any] | None = None,
|
|
66
|
+
box_config: dict[str, Any] | None = None,
|
|
67
|
+
frame_config: dict[str, Any] | None = None,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> None:
|
|
70
|
+
super().__init__(**kwargs)
|
|
71
|
+
|
|
72
|
+
# Setup Defaults
|
|
73
|
+
default_label_config: dict[str, Any] = {
|
|
74
|
+
"color": WHITE,
|
|
75
|
+
"font_size": DEFAULT_FONT_SIZE,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
default_box_config: dict[str, Any] = {
|
|
79
|
+
"color": None,
|
|
80
|
+
"buff": 0.05,
|
|
81
|
+
"fill_opacity": 1,
|
|
82
|
+
"stroke_width": 0.5,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
default_frame_config: dict[str, Any] = {
|
|
86
|
+
"color": WHITE,
|
|
87
|
+
"buff": 0.05,
|
|
88
|
+
"stroke_width": 0.5,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Merge Defaults
|
|
92
|
+
label_config = default_label_config | (label_config or {})
|
|
93
|
+
box_config = default_box_config | (box_config or {})
|
|
94
|
+
frame_config = default_frame_config | (frame_config or {})
|
|
95
|
+
|
|
96
|
+
# Determine the type of label and instantiate the appropriate object
|
|
97
|
+
self.rendered_label: MathTex | Tex | Text
|
|
98
|
+
if isinstance(label, str):
|
|
99
|
+
self.rendered_label = MathTex(label, **label_config)
|
|
100
|
+
elif isinstance(label, (MathTex, Tex, Text)):
|
|
101
|
+
self.rendered_label = label
|
|
102
|
+
else:
|
|
103
|
+
raise TypeError("Unsupported label type. Must be MathTex, Tex, or Text.")
|
|
104
|
+
|
|
105
|
+
# Add a background box
|
|
106
|
+
self.background_rect = BackgroundRectangle(self.rendered_label, **box_config)
|
|
107
|
+
|
|
108
|
+
# Add a frame around the label
|
|
109
|
+
self.frame = SurroundingRectangle(self.rendered_label, **frame_config)
|
|
110
|
+
|
|
111
|
+
# Add components to the VGroup
|
|
112
|
+
self.add(self.background_rect, self.rendered_label, self.frame)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LabeledLine(Line):
|
|
116
|
+
"""Constructs a line containing a label box somewhere along its length.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
label
|
|
121
|
+
Label that will be displayed on the line.
|
|
122
|
+
label_position
|
|
123
|
+
A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5.
|
|
124
|
+
label_config
|
|
125
|
+
A dictionary containing the configuration for the label.
|
|
126
|
+
This is only applied if ``label`` is of type ``str``.
|
|
127
|
+
box_config
|
|
128
|
+
A dictionary containing the configuration for the background box.
|
|
129
|
+
frame_config
|
|
130
|
+
A dictionary containing the configuration for the frame.
|
|
131
|
+
|
|
132
|
+
.. seealso::
|
|
133
|
+
:class:`LabeledArrow`
|
|
134
|
+
|
|
135
|
+
Examples
|
|
136
|
+
--------
|
|
137
|
+
.. manim:: LabeledLineExample
|
|
138
|
+
:save_last_frame:
|
|
139
|
+
:quality: high
|
|
140
|
+
|
|
141
|
+
class LabeledLineExample(Scene):
|
|
142
|
+
def construct(self):
|
|
143
|
+
line = LabeledLine(
|
|
144
|
+
label = '0.5',
|
|
145
|
+
label_position = 0.8,
|
|
146
|
+
label_config = {
|
|
147
|
+
"font_size" : 20
|
|
148
|
+
},
|
|
149
|
+
start=LEFT+DOWN,
|
|
150
|
+
end=RIGHT+UP)
|
|
151
|
+
|
|
152
|
+
line.set_length(line.get_length() * 2)
|
|
153
|
+
self.add(line)
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
def __init__(
|
|
157
|
+
self,
|
|
158
|
+
label: str | Tex | MathTex | Text,
|
|
159
|
+
label_position: float = 0.5,
|
|
160
|
+
label_config: dict[str, Any] | None = None,
|
|
161
|
+
box_config: dict[str, Any] | None = None,
|
|
162
|
+
frame_config: dict[str, Any] | None = None,
|
|
163
|
+
*args: Any,
|
|
164
|
+
**kwargs: Any,
|
|
165
|
+
) -> None:
|
|
166
|
+
super().__init__(*args, **kwargs)
|
|
167
|
+
|
|
168
|
+
# Create Label
|
|
169
|
+
self.label = Label(
|
|
170
|
+
label=label,
|
|
171
|
+
label_config=label_config,
|
|
172
|
+
box_config=box_config,
|
|
173
|
+
frame_config=frame_config,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Compute Label Position
|
|
177
|
+
line_start, line_end = self.get_start_and_end()
|
|
178
|
+
new_vec = (line_end - line_start) * label_position
|
|
179
|
+
label_coords = line_start + new_vec
|
|
180
|
+
|
|
181
|
+
self.label.move_to(label_coords)
|
|
182
|
+
self.add(self.label)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class LabeledArrow(LabeledLine, Arrow):
|
|
186
|
+
"""Constructs an arrow containing a label box somewhere along its length.
|
|
187
|
+
This class inherits its label properties from `LabeledLine`, so the main parameters controlling it are the same.
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
label
|
|
192
|
+
Label that will be displayed on the Arrow.
|
|
193
|
+
label_position
|
|
194
|
+
A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5.
|
|
195
|
+
label_config
|
|
196
|
+
A dictionary containing the configuration for the label.
|
|
197
|
+
This is only applied if ``label`` is of type ``str``.
|
|
198
|
+
box_config
|
|
199
|
+
A dictionary containing the configuration for the background box.
|
|
200
|
+
frame_config
|
|
201
|
+
A dictionary containing the configuration for the frame.
|
|
202
|
+
|
|
203
|
+
.. seealso::
|
|
204
|
+
:class:`LabeledLine`
|
|
205
|
+
|
|
206
|
+
Examples
|
|
207
|
+
--------
|
|
208
|
+
.. manim:: LabeledArrowExample
|
|
209
|
+
:save_last_frame:
|
|
210
|
+
:quality: high
|
|
211
|
+
|
|
212
|
+
class LabeledArrowExample(Scene):
|
|
213
|
+
def construct(self):
|
|
214
|
+
l_arrow = LabeledArrow("0.5", start=LEFT*3, end=RIGHT*3 + UP*2, label_position=0.5)
|
|
215
|
+
|
|
216
|
+
self.add(l_arrow)
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
def __init__(
|
|
220
|
+
self,
|
|
221
|
+
*args: Any,
|
|
222
|
+
**kwargs: Any,
|
|
223
|
+
) -> None:
|
|
224
|
+
super().__init__(*args, **kwargs)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class LabeledPolygram(Polygram):
|
|
228
|
+
"""Constructs a polygram containing a label box at its pole of inaccessibility.
|
|
229
|
+
|
|
230
|
+
Parameters
|
|
231
|
+
----------
|
|
232
|
+
vertex_groups
|
|
233
|
+
Vertices passed to the :class:`~.Polygram` constructor.
|
|
234
|
+
label
|
|
235
|
+
Label that will be displayed on the Polygram.
|
|
236
|
+
precision
|
|
237
|
+
The precision used by the PolyLabel algorithm.
|
|
238
|
+
label_config
|
|
239
|
+
A dictionary containing the configuration for the label.
|
|
240
|
+
This is only applied if ``label`` is of type ``str``.
|
|
241
|
+
box_config
|
|
242
|
+
A dictionary containing the configuration for the background box.
|
|
243
|
+
frame_config
|
|
244
|
+
A dictionary containing the configuration for the frame.
|
|
245
|
+
|
|
246
|
+
.. note::
|
|
247
|
+
The PolyLabel Algorithm expects each vertex group to form a closed ring.
|
|
248
|
+
If the input is open, :class:`LabeledPolygram` will attempt to close it.
|
|
249
|
+
This may cause the polygon to intersect itself leading to unexpected results.
|
|
250
|
+
|
|
251
|
+
.. tip::
|
|
252
|
+
Make sure the precision corresponds to the scale of your inputs!
|
|
253
|
+
For instance, if the bounding box of your polygon stretches from 0 to 10,000, a precision of 1.0 or 10.0 should be sufficient.
|
|
254
|
+
|
|
255
|
+
Examples
|
|
256
|
+
--------
|
|
257
|
+
.. manim:: LabeledPolygramExample
|
|
258
|
+
:save_last_frame:
|
|
259
|
+
:quality: high
|
|
260
|
+
|
|
261
|
+
class LabeledPolygramExample(Scene):
|
|
262
|
+
def construct(self):
|
|
263
|
+
# Define Rings
|
|
264
|
+
ring1 = [
|
|
265
|
+
[-3.8, -2.4, 0], [-2.4, -2.5, 0], [-1.3, -1.6, 0], [-0.2, -1.7, 0],
|
|
266
|
+
[1.7, -2.5, 0], [2.9, -2.6, 0], [3.5, -1.5, 0], [4.9, -1.4, 0],
|
|
267
|
+
[4.5, 0.2, 0], [4.7, 1.6, 0], [3.5, 2.4, 0], [1.1, 2.5, 0],
|
|
268
|
+
[-0.1, 0.9, 0], [-1.2, 0.5, 0], [-1.6, 0.7, 0], [-1.4, 1.9, 0],
|
|
269
|
+
[-2.6, 2.6, 0], [-4.4, 1.2, 0], [-4.9, -0.8, 0], [-3.8, -2.4, 0]
|
|
270
|
+
]
|
|
271
|
+
ring2 = [
|
|
272
|
+
[0.2, -1.2, 0], [0.9, -1.2, 0], [1.4, -2.0, 0], [2.1, -1.6, 0],
|
|
273
|
+
[2.2, -0.5, 0], [1.4, 0.0, 0], [0.4, -0.2, 0], [0.2, -1.2, 0]
|
|
274
|
+
]
|
|
275
|
+
ring3 = [[-2.7, 1.4, 0], [-2.3, 1.7, 0], [-2.8, 1.9, 0], [-2.7, 1.4, 0]]
|
|
276
|
+
|
|
277
|
+
# Create Polygons (for reference)
|
|
278
|
+
p1 = Polygon(*ring1, fill_opacity=0.75)
|
|
279
|
+
p2 = Polygon(*ring2, fill_color=BLACK, fill_opacity=1)
|
|
280
|
+
p3 = Polygon(*ring3, fill_color=BLACK, fill_opacity=1)
|
|
281
|
+
|
|
282
|
+
# Create Labeled Polygram
|
|
283
|
+
polygram = LabeledPolygram(
|
|
284
|
+
*[ring1, ring2, ring3],
|
|
285
|
+
label=Text('Pole', font='sans-serif'),
|
|
286
|
+
precision=0.01,
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
# Display Circle (for reference)
|
|
290
|
+
circle = Circle(radius=polygram.radius, color=WHITE).move_to(polygram.pole)
|
|
291
|
+
|
|
292
|
+
self.add(p1, p2, p3)
|
|
293
|
+
self.add(polygram)
|
|
294
|
+
self.add(circle)
|
|
295
|
+
|
|
296
|
+
.. manim:: LabeledCountryExample
|
|
297
|
+
:save_last_frame:
|
|
298
|
+
:quality: high
|
|
299
|
+
|
|
300
|
+
import requests
|
|
301
|
+
import json
|
|
302
|
+
|
|
303
|
+
class LabeledCountryExample(Scene):
|
|
304
|
+
def construct(self):
|
|
305
|
+
# Fetch JSON data and process arcs
|
|
306
|
+
data = requests.get('https://cdn.jsdelivr.net/npm/us-atlas@3/nation-10m.json').json()
|
|
307
|
+
arcs, transform = data['arcs'], data['transform']
|
|
308
|
+
sarcs = [np.cumsum(arc, axis=0) * transform['scale'] + transform['translate'] for arc in arcs]
|
|
309
|
+
ssarcs = sorted(sarcs, key=len, reverse=True)[:1]
|
|
310
|
+
|
|
311
|
+
# Compute Bounding Box
|
|
312
|
+
points = np.concatenate(ssarcs)
|
|
313
|
+
mins, maxs = np.min(points, axis=0), np.max(points, axis=0)
|
|
314
|
+
|
|
315
|
+
# Build Axes
|
|
316
|
+
ax = Axes(
|
|
317
|
+
x_range=[mins[0], maxs[0], maxs[0] - mins[0]], x_length=10,
|
|
318
|
+
y_range=[mins[1], maxs[1], maxs[1] - mins[1]], y_length=7,
|
|
319
|
+
tips=False
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Adjust Coordinates
|
|
323
|
+
array = [[ax.c2p(*point) for point in sarc] for sarc in ssarcs]
|
|
324
|
+
|
|
325
|
+
# Add Polygram
|
|
326
|
+
polygram = LabeledPolygram(
|
|
327
|
+
*array,
|
|
328
|
+
label=Text('USA', font='sans-serif'),
|
|
329
|
+
precision=0.01,
|
|
330
|
+
fill_color=BLUE,
|
|
331
|
+
stroke_width=0,
|
|
332
|
+
fill_opacity=0.75
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Display Circle (for reference)
|
|
336
|
+
circle = Circle(radius=polygram.radius, color=WHITE).move_to(polygram.pole)
|
|
337
|
+
|
|
338
|
+
self.add(ax)
|
|
339
|
+
self.add(polygram)
|
|
340
|
+
self.add(circle)
|
|
341
|
+
"""
|
|
342
|
+
|
|
343
|
+
def __init__(
|
|
344
|
+
self,
|
|
345
|
+
*vertex_groups: Point3DLike_Array,
|
|
346
|
+
label: str | Tex | MathTex | Text,
|
|
347
|
+
precision: float = 0.01,
|
|
348
|
+
label_config: dict[str, Any] | None = None,
|
|
349
|
+
box_config: dict[str, Any] | None = None,
|
|
350
|
+
frame_config: dict[str, Any] | None = None,
|
|
351
|
+
**kwargs: Any,
|
|
352
|
+
) -> None:
|
|
353
|
+
# Initialize the Polygram with the vertex groups
|
|
354
|
+
super().__init__(*vertex_groups, **kwargs)
|
|
355
|
+
|
|
356
|
+
# Create Label
|
|
357
|
+
self.label = Label(
|
|
358
|
+
label=label,
|
|
359
|
+
label_config=label_config,
|
|
360
|
+
box_config=box_config,
|
|
361
|
+
frame_config=frame_config,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# Close Vertex Groups
|
|
365
|
+
rings = [
|
|
366
|
+
group if np.array_equal(group[0], group[-1]) else list(group) + [group[0]]
|
|
367
|
+
for group in vertex_groups
|
|
368
|
+
]
|
|
369
|
+
|
|
370
|
+
# Compute the Pole of Inaccessibility
|
|
371
|
+
cell = polylabel(rings, precision=precision)
|
|
372
|
+
self.pole, self.radius = np.pad(cell.c, (0, 1), "constant"), cell.d
|
|
373
|
+
|
|
374
|
+
# Position the label at the pole
|
|
375
|
+
self.label.move_to(self.pole)
|
|
376
|
+
self.add(self.label)
|