manim 0.18.1__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of manim might be problematic. Click here for more details.
- manim/__main__.py +45 -12
- manim/_config/__init__.py +2 -2
- manim/_config/cli_colors.py +8 -4
- manim/_config/default.cfg +0 -2
- manim/_config/logger_utils.py +5 -0
- manim/_config/utils.py +29 -38
- manim/animation/animation.py +148 -8
- manim/animation/composition.py +16 -13
- manim/animation/creation.py +184 -8
- manim/animation/fading.py +5 -8
- manim/animation/indication.py +93 -26
- manim/animation/movement.py +21 -3
- manim/animation/rotation.py +2 -1
- manim/animation/specialized.py +3 -5
- manim/animation/speedmodifier.py +3 -3
- manim/animation/transform.py +4 -5
- manim/animation/updaters/mobject_update_utils.py +17 -14
- manim/camera/camera.py +2 -2
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +52 -36
- manim/cli/checkhealth/checks.py +92 -76
- manim/cli/checkhealth/commands.py +12 -5
- manim/cli/default_group.py +148 -24
- manim/cli/init/commands.py +28 -23
- manim/cli/plugins/commands.py +13 -3
- manim/cli/render/commands.py +47 -42
- manim/cli/render/global_options.py +43 -9
- manim/cli/render/render_options.py +84 -19
- manim/constants.py +11 -4
- manim/mobject/frame.py +0 -1
- manim/mobject/geometry/arc.py +109 -75
- manim/mobject/geometry/boolean_ops.py +20 -17
- manim/mobject/geometry/labeled.py +300 -77
- manim/mobject/geometry/line.py +120 -60
- manim/mobject/geometry/polygram.py +109 -25
- manim/mobject/geometry/shape_matchers.py +35 -15
- manim/mobject/geometry/tips.py +36 -27
- manim/mobject/graph.py +48 -40
- manim/mobject/graphing/coordinate_systems.py +110 -45
- manim/mobject/graphing/functions.py +16 -10
- manim/mobject/graphing/number_line.py +23 -9
- manim/mobject/graphing/probability.py +2 -10
- manim/mobject/graphing/scale.py +6 -5
- manim/mobject/matrix.py +17 -19
- manim/mobject/mobject.py +149 -103
- manim/mobject/opengl/opengl_geometry.py +4 -8
- manim/mobject/opengl/opengl_mobject.py +506 -343
- manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
- manim/mobject/opengl/opengl_surface.py +1 -2
- manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
- manim/mobject/svg/brace.py +61 -13
- manim/mobject/svg/svg_mobject.py +2 -1
- manim/mobject/table.py +11 -12
- manim/mobject/text/code_mobject.py +186 -550
- manim/mobject/text/numbers.py +7 -7
- manim/mobject/text/tex_mobject.py +22 -13
- manim/mobject/text/text_mobject.py +29 -20
- manim/mobject/three_d/polyhedra.py +98 -1
- manim/mobject/three_d/three_dimensions.py +59 -31
- manim/mobject/types/image_mobject.py +37 -23
- manim/mobject/types/point_cloud_mobject.py +103 -67
- manim/mobject/types/vectorized_mobject.py +387 -214
- manim/mobject/value_tracker.py +2 -1
- manim/mobject/vector_field.py +2 -4
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +2 -3
- manim/plugins/plugins_flags.py +3 -3
- manim/renderer/cairo_renderer.py +11 -11
- manim/renderer/opengl_renderer.py +19 -20
- manim/renderer/shader.py +2 -3
- manim/renderer/shader_wrapper.py +3 -2
- manim/scene/moving_camera_scene.py +23 -0
- manim/scene/scene.py +72 -41
- manim/scene/scene_file_writer.py +313 -164
- manim/scene/section.py +15 -15
- manim/scene/three_d_scene.py +8 -15
- manim/scene/vector_space_scene.py +3 -6
- manim/typing.py +326 -66
- manim/utils/bezier.py +1658 -381
- manim/utils/caching.py +11 -5
- manim/utils/color/AS2700.py +2 -0
- manim/utils/color/BS381.py +2 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +3 -0
- manim/utils/color/XKCD.py +2 -0
- manim/utils/color/__init__.py +8 -5
- manim/utils/color/core.py +818 -301
- manim/utils/color/manim_colors.py +7 -9
- manim/utils/commands.py +40 -19
- manim/utils/config_ops.py +18 -13
- manim/utils/debug.py +8 -6
- manim/utils/deprecation.py +92 -43
- manim/utils/docbuild/autoaliasattr_directive.py +45 -8
- manim/utils/docbuild/autocolor_directive.py +12 -13
- manim/utils/docbuild/manim_directive.py +35 -29
- manim/utils/docbuild/module_parsing.py +74 -27
- manim/utils/family.py +3 -3
- manim/utils/family_ops.py +12 -4
- manim/utils/file_ops.py +22 -16
- manim/utils/hashing.py +7 -7
- manim/utils/images.py +10 -4
- manim/utils/ipython_magic.py +12 -8
- manim/utils/iterables.py +161 -119
- manim/utils/module_ops.py +55 -19
- manim/utils/opengl.py +68 -23
- manim/utils/parameter_parsing.py +3 -2
- manim/utils/paths.py +11 -5
- manim/utils/polylabel.py +168 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +69 -32
- manim/utils/simple_functions.py +24 -15
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +48 -37
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +33 -18
- manim/utils/testing/frames_comparison.py +20 -14
- manim/utils/tex.py +4 -2
- manim/utils/tex_file_writing.py +45 -45
- manim/utils/tex_templates.py +1 -1
- manim/utils/unit.py +6 -5
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
- manim-0.19.0.dist-info/RECORD +221 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
- manim-0.18.1.dist-info/RECORD +0 -217
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
|
@@ -13,7 +13,9 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|
|
13
13
|
from manim.mobject.types.vectorized_mobject import VMobject
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
|
-
from
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from manim.typing import Point2DLike_Array, Point3D_Array, Point3DLike_Array
|
|
17
19
|
|
|
18
20
|
from ...constants import RendererType
|
|
19
21
|
|
|
@@ -28,7 +30,7 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
28
30
|
|
|
29
31
|
def _convert_2d_to_3d_array(
|
|
30
32
|
self,
|
|
31
|
-
points:
|
|
33
|
+
points: Point2DLike_Array | Point3DLike_Array,
|
|
32
34
|
z_dim: float = 0.0,
|
|
33
35
|
) -> Point3D_Array:
|
|
34
36
|
"""Converts an iterable with coordinates in 2D to 3D by adding
|
|
@@ -36,9 +38,9 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
36
38
|
|
|
37
39
|
Parameters
|
|
38
40
|
----------
|
|
39
|
-
points
|
|
41
|
+
points
|
|
40
42
|
An iterable of points.
|
|
41
|
-
z_dim
|
|
43
|
+
z_dim
|
|
42
44
|
Default value for the Z coordinate.
|
|
43
45
|
|
|
44
46
|
Returns
|
|
@@ -51,13 +53,14 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
51
53
|
>>> a = _BooleanOps()
|
|
52
54
|
>>> p = [(1, 2), (3, 4)]
|
|
53
55
|
>>> a._convert_2d_to_3d_array(p)
|
|
54
|
-
|
|
56
|
+
array([[1., 2., 0.],
|
|
57
|
+
[3., 4., 0.]])
|
|
55
58
|
"""
|
|
56
|
-
|
|
57
|
-
for i, point in enumerate(
|
|
59
|
+
list_of_points = list(points)
|
|
60
|
+
for i, point in enumerate(list_of_points):
|
|
58
61
|
if len(point) == 2:
|
|
59
|
-
|
|
60
|
-
return
|
|
62
|
+
list_of_points[i] = np.array(list(point) + [z_dim])
|
|
63
|
+
return np.asarray(list_of_points)
|
|
61
64
|
|
|
62
65
|
def _convert_vmobject_to_skia_path(self, vmobject: VMobject) -> SkiaPath:
|
|
63
66
|
"""Converts a :class:`~.VMobject` to SkiaPath. This method only works for
|
|
@@ -90,17 +93,17 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
90
93
|
quads = vmobject.get_bezier_tuples_from_points(subpath)
|
|
91
94
|
start = subpath[0]
|
|
92
95
|
path.moveTo(*start[:2])
|
|
93
|
-
for
|
|
96
|
+
for _p0, p1, p2 in quads:
|
|
94
97
|
path.quadTo(*p1[:2], *p2[:2])
|
|
95
98
|
if vmobject.consider_points_equals(subpath[0], subpath[-1]):
|
|
96
99
|
path.close()
|
|
97
100
|
elif config.renderer == RendererType.CAIRO:
|
|
98
|
-
subpaths = vmobject.gen_subpaths_from_points_2d(points)
|
|
101
|
+
subpaths = vmobject.gen_subpaths_from_points_2d(points) # type: ignore[assignment]
|
|
99
102
|
for subpath in subpaths:
|
|
100
103
|
quads = vmobject.gen_cubic_bezier_tuples_from_points(subpath)
|
|
101
104
|
start = subpath[0]
|
|
102
105
|
path.moveTo(*start[:2])
|
|
103
|
-
for
|
|
106
|
+
for _p0, p1, p2, p3 in quads:
|
|
104
107
|
path.cubicTo(*p1[:2], *p2[:2], *p3[:2])
|
|
105
108
|
|
|
106
109
|
if vmobject.consider_points_equals_2d(subpath[0], subpath[-1]):
|
|
@@ -142,7 +145,7 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|
|
142
145
|
n1, n2 = self._convert_2d_to_3d_array(points)
|
|
143
146
|
vmobject.add_quadratic_bezier_curve_to(n1, n2)
|
|
144
147
|
else:
|
|
145
|
-
raise Exception("Unsupported:
|
|
148
|
+
raise Exception(f"Unsupported: {path_verb}")
|
|
146
149
|
return vmobject
|
|
147
150
|
|
|
148
151
|
|
|
@@ -177,7 +180,7 @@ class Union(_BooleanOps):
|
|
|
177
180
|
|
|
178
181
|
"""
|
|
179
182
|
|
|
180
|
-
def __init__(self, *vmobjects: VMobject, **kwargs) -> None:
|
|
183
|
+
def __init__(self, *vmobjects: VMobject, **kwargs: Any) -> None:
|
|
181
184
|
if len(vmobjects) < 2:
|
|
182
185
|
raise ValueError("At least 2 mobjects needed for Union.")
|
|
183
186
|
super().__init__(**kwargs)
|
|
@@ -216,7 +219,7 @@ class Difference(_BooleanOps):
|
|
|
216
219
|
|
|
217
220
|
"""
|
|
218
221
|
|
|
219
|
-
def __init__(self, subject: VMobject, clip: VMobject, **kwargs) -> None:
|
|
222
|
+
def __init__(self, subject: VMobject, clip: VMobject, **kwargs: Any) -> None:
|
|
220
223
|
super().__init__(**kwargs)
|
|
221
224
|
outpen = SkiaPath()
|
|
222
225
|
difference(
|
|
@@ -258,7 +261,7 @@ class Intersection(_BooleanOps):
|
|
|
258
261
|
|
|
259
262
|
"""
|
|
260
263
|
|
|
261
|
-
def __init__(self, *vmobjects: VMobject, **kwargs) -> None:
|
|
264
|
+
def __init__(self, *vmobjects: VMobject, **kwargs: Any) -> None:
|
|
262
265
|
if len(vmobjects) < 2:
|
|
263
266
|
raise ValueError("At least 2 mobjects needed for Intersection.")
|
|
264
267
|
|
|
@@ -311,7 +314,7 @@ class Exclusion(_BooleanOps):
|
|
|
311
314
|
|
|
312
315
|
"""
|
|
313
316
|
|
|
314
|
-
def __init__(self, subject: VMobject, clip: VMobject, **kwargs) -> None:
|
|
317
|
+
def __init__(self, subject: VMobject, clip: VMobject, **kwargs: Any) -> None:
|
|
315
318
|
super().__init__(**kwargs)
|
|
316
319
|
outpen = SkiaPath()
|
|
317
320
|
xor(
|
|
@@ -2,17 +2,116 @@ r"""Mobjects that inherit from lines and contain a label along the length."""
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__all__ = ["LabeledLine", "LabeledArrow"]
|
|
5
|
+
__all__ = ["Label", "LabeledLine", "LabeledArrow", "LabeledPolygram"]
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
6
10
|
|
|
7
11
|
from manim.constants import *
|
|
8
12
|
from manim.mobject.geometry.line import Arrow, Line
|
|
13
|
+
from manim.mobject.geometry.polygram import Polygram
|
|
9
14
|
from manim.mobject.geometry.shape_matchers import (
|
|
10
15
|
BackgroundRectangle,
|
|
11
16
|
SurroundingRectangle,
|
|
12
17
|
)
|
|
13
18
|
from manim.mobject.text.tex_mobject import MathTex, Tex
|
|
14
19
|
from manim.mobject.text.text_mobject import Text
|
|
15
|
-
from manim.
|
|
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 typing import Any
|
|
26
|
+
|
|
27
|
+
from manim.typing import Point3DLike_Array
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Label(VGroup):
|
|
31
|
+
"""A Label consisting of text surrounded by a frame.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
label
|
|
36
|
+
Label that will be displayed.
|
|
37
|
+
label_config
|
|
38
|
+
A dictionary containing the configuration for the label.
|
|
39
|
+
This is only applied if ``label`` is of type ``str``.
|
|
40
|
+
box_config
|
|
41
|
+
A dictionary containing the configuration for the background box.
|
|
42
|
+
frame_config
|
|
43
|
+
A dictionary containing the configuration for the frame.
|
|
44
|
+
|
|
45
|
+
Examples
|
|
46
|
+
--------
|
|
47
|
+
.. manim:: LabelExample
|
|
48
|
+
:save_last_frame:
|
|
49
|
+
:quality: high
|
|
50
|
+
|
|
51
|
+
class LabelExample(Scene):
|
|
52
|
+
def construct(self):
|
|
53
|
+
label = Label(
|
|
54
|
+
label=Text('Label Text', font='sans-serif'),
|
|
55
|
+
box_config = {
|
|
56
|
+
"color" : BLUE,
|
|
57
|
+
"fill_opacity" : 0.75
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
label.scale(3)
|
|
61
|
+
self.add(label)
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
label: str | Tex | MathTex | Text,
|
|
67
|
+
label_config: dict[str, Any] | None = None,
|
|
68
|
+
box_config: dict[str, Any] | None = None,
|
|
69
|
+
frame_config: dict[str, Any] | None = None,
|
|
70
|
+
**kwargs: Any,
|
|
71
|
+
) -> None:
|
|
72
|
+
super().__init__(**kwargs)
|
|
73
|
+
|
|
74
|
+
# Setup Defaults
|
|
75
|
+
default_label_config: dict[str, Any] = {
|
|
76
|
+
"color": WHITE,
|
|
77
|
+
"font_size": DEFAULT_FONT_SIZE,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
default_box_config: dict[str, Any] = {
|
|
81
|
+
"color": None,
|
|
82
|
+
"buff": 0.05,
|
|
83
|
+
"fill_opacity": 1,
|
|
84
|
+
"stroke_width": 0.5,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
default_frame_config: dict[str, Any] = {
|
|
88
|
+
"color": WHITE,
|
|
89
|
+
"buff": 0.05,
|
|
90
|
+
"stroke_width": 0.5,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Merge Defaults
|
|
94
|
+
label_config = default_label_config | (label_config or {})
|
|
95
|
+
box_config = default_box_config | (box_config or {})
|
|
96
|
+
frame_config = default_frame_config | (frame_config or {})
|
|
97
|
+
|
|
98
|
+
# Determine the type of label and instantiate the appropriate object
|
|
99
|
+
self.rendered_label: MathTex | Tex | Text
|
|
100
|
+
if isinstance(label, str):
|
|
101
|
+
self.rendered_label = MathTex(label, **label_config)
|
|
102
|
+
elif isinstance(label, (MathTex, Tex, Text)):
|
|
103
|
+
self.rendered_label = label
|
|
104
|
+
else:
|
|
105
|
+
raise TypeError("Unsupported label type. Must be MathTex, Tex, or Text.")
|
|
106
|
+
|
|
107
|
+
# Add a background box
|
|
108
|
+
self.background_rect = BackgroundRectangle(self.rendered_label, **box_config)
|
|
109
|
+
|
|
110
|
+
# Add a frame around the label
|
|
111
|
+
self.frame = SurroundingRectangle(self.rendered_label, **frame_config)
|
|
112
|
+
|
|
113
|
+
# Add components to the VGroup
|
|
114
|
+
self.add(self.background_rect, self.rendered_label, self.frame)
|
|
16
115
|
|
|
17
116
|
|
|
18
117
|
class LabeledLine(Line):
|
|
@@ -20,42 +119,38 @@ class LabeledLine(Line):
|
|
|
20
119
|
|
|
21
120
|
Parameters
|
|
22
121
|
----------
|
|
23
|
-
label
|
|
122
|
+
label
|
|
24
123
|
Label that will be displayed on the line.
|
|
25
|
-
label_position
|
|
124
|
+
label_position
|
|
26
125
|
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.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
.. seealso::
|
|
39
|
-
:class:`LabeledArrow`
|
|
126
|
+
label_config
|
|
127
|
+
A dictionary containing the configuration for the label.
|
|
128
|
+
This is only applied if ``label`` is of type ``str``.
|
|
129
|
+
box_config
|
|
130
|
+
A dictionary containing the configuration for the background box.
|
|
131
|
+
frame_config
|
|
132
|
+
A dictionary containing the configuration for the frame.
|
|
133
|
+
|
|
134
|
+
.. seealso::
|
|
135
|
+
:class:`LabeledArrow`
|
|
40
136
|
|
|
41
137
|
Examples
|
|
42
138
|
--------
|
|
43
139
|
.. manim:: LabeledLineExample
|
|
44
140
|
:save_last_frame:
|
|
141
|
+
:quality: high
|
|
45
142
|
|
|
46
143
|
class LabeledLineExample(Scene):
|
|
47
144
|
def construct(self):
|
|
48
145
|
line = LabeledLine(
|
|
49
146
|
label = '0.5',
|
|
50
147
|
label_position = 0.8,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
148
|
+
label_config = {
|
|
149
|
+
"font_size" : 20
|
|
150
|
+
},
|
|
55
151
|
start=LEFT+DOWN,
|
|
56
152
|
end=RIGHT+UP)
|
|
57
153
|
|
|
58
|
-
|
|
59
154
|
line.set_length(line.get_length() * 2)
|
|
60
155
|
self.add(line)
|
|
61
156
|
"""
|
|
@@ -64,50 +159,29 @@ class LabeledLine(Line):
|
|
|
64
159
|
self,
|
|
65
160
|
label: str | Tex | MathTex | Text,
|
|
66
161
|
label_position: float = 0.5,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
*args,
|
|
73
|
-
**kwargs,
|
|
162
|
+
label_config: dict[str, Any] | None = None,
|
|
163
|
+
box_config: dict[str, Any] | None = None,
|
|
164
|
+
frame_config: dict[str, Any] | None = None,
|
|
165
|
+
*args: Any,
|
|
166
|
+
**kwargs: Any,
|
|
74
167
|
) -> None:
|
|
75
|
-
label_color = ManimColor(label_color)
|
|
76
|
-
frame_fill_color = ManimColor(frame_fill_color)
|
|
77
|
-
if isinstance(label, str):
|
|
78
|
-
from manim import MathTex
|
|
79
|
-
|
|
80
|
-
rendered_label = MathTex(label, color=label_color, font_size=font_size)
|
|
81
|
-
else:
|
|
82
|
-
rendered_label = label
|
|
83
|
-
|
|
84
168
|
super().__init__(*args, **kwargs)
|
|
85
169
|
|
|
86
|
-
#
|
|
170
|
+
# Create Label
|
|
171
|
+
self.label = Label(
|
|
172
|
+
label=label,
|
|
173
|
+
label_config=label_config,
|
|
174
|
+
box_config=box_config,
|
|
175
|
+
frame_config=frame_config,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Compute Label Position
|
|
87
179
|
line_start, line_end = self.get_start_and_end()
|
|
88
180
|
new_vec = (line_end - line_start) * label_position
|
|
89
181
|
label_coords = line_start + new_vec
|
|
90
182
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
box = BackgroundRectangle(
|
|
95
|
-
rendered_label,
|
|
96
|
-
buff=0.05,
|
|
97
|
-
color=frame_fill_color,
|
|
98
|
-
fill_opacity=frame_fill_opacity,
|
|
99
|
-
stroke_width=0.5,
|
|
100
|
-
)
|
|
101
|
-
self.add(box)
|
|
102
|
-
|
|
103
|
-
if label_frame:
|
|
104
|
-
box_frame = SurroundingRectangle(
|
|
105
|
-
rendered_label, buff=0.05, color=label_color, stroke_width=0.5
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
self.add(box_frame)
|
|
109
|
-
|
|
110
|
-
self.add(rendered_label)
|
|
183
|
+
self.label.move_to(label_coords)
|
|
184
|
+
self.add(self.label)
|
|
111
185
|
|
|
112
186
|
|
|
113
187
|
class LabeledArrow(LabeledLine, Arrow):
|
|
@@ -116,29 +190,26 @@ class LabeledArrow(LabeledLine, Arrow):
|
|
|
116
190
|
|
|
117
191
|
Parameters
|
|
118
192
|
----------
|
|
119
|
-
label
|
|
120
|
-
Label that will be displayed on the
|
|
121
|
-
label_position
|
|
193
|
+
label
|
|
194
|
+
Label that will be displayed on the Arrow.
|
|
195
|
+
label_position
|
|
122
196
|
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.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
Background color to fill the label box. If no value is provided, the background color of the canvas will be used.
|
|
131
|
-
frame_fill_opacity : float | optional
|
|
132
|
-
Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity.
|
|
197
|
+
label_config
|
|
198
|
+
A dictionary containing the configuration for the label.
|
|
199
|
+
This is only applied if ``label`` is of type ``str``.
|
|
200
|
+
box_config
|
|
201
|
+
A dictionary containing the configuration for the background box.
|
|
202
|
+
frame_config
|
|
203
|
+
A dictionary containing the configuration for the frame.
|
|
133
204
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
:class:`LabeledLine`
|
|
205
|
+
.. seealso::
|
|
206
|
+
:class:`LabeledLine`
|
|
137
207
|
|
|
138
208
|
Examples
|
|
139
209
|
--------
|
|
140
210
|
.. manim:: LabeledArrowExample
|
|
141
211
|
:save_last_frame:
|
|
212
|
+
:quality: high
|
|
142
213
|
|
|
143
214
|
class LabeledArrowExample(Scene):
|
|
144
215
|
def construct(self):
|
|
@@ -149,7 +220,159 @@ class LabeledArrow(LabeledLine, Arrow):
|
|
|
149
220
|
|
|
150
221
|
def __init__(
|
|
151
222
|
self,
|
|
152
|
-
*args,
|
|
153
|
-
**kwargs,
|
|
223
|
+
*args: Any,
|
|
224
|
+
**kwargs: Any,
|
|
154
225
|
) -> None:
|
|
155
226
|
super().__init__(*args, **kwargs)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class LabeledPolygram(Polygram):
|
|
230
|
+
"""Constructs a polygram containing a label box at its pole of inaccessibility.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
vertex_groups
|
|
235
|
+
Vertices passed to the :class:`~.Polygram` constructor.
|
|
236
|
+
label
|
|
237
|
+
Label that will be displayed on the Polygram.
|
|
238
|
+
precision
|
|
239
|
+
The precision used by the PolyLabel algorithm.
|
|
240
|
+
label_config
|
|
241
|
+
A dictionary containing the configuration for the label.
|
|
242
|
+
This is only applied if ``label`` is of type ``str``.
|
|
243
|
+
box_config
|
|
244
|
+
A dictionary containing the configuration for the background box.
|
|
245
|
+
frame_config
|
|
246
|
+
A dictionary containing the configuration for the frame.
|
|
247
|
+
|
|
248
|
+
.. note::
|
|
249
|
+
The PolyLabel Algorithm expects each vertex group to form a closed ring.
|
|
250
|
+
If the input is open, :class:`LabeledPolygram` will attempt to close it.
|
|
251
|
+
This may cause the polygon to intersect itself leading to unexpected results.
|
|
252
|
+
|
|
253
|
+
.. tip::
|
|
254
|
+
Make sure the precision corresponds to the scale of your inputs!
|
|
255
|
+
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.
|
|
256
|
+
|
|
257
|
+
Examples
|
|
258
|
+
--------
|
|
259
|
+
.. manim:: LabeledPolygramExample
|
|
260
|
+
:save_last_frame:
|
|
261
|
+
:quality: high
|
|
262
|
+
|
|
263
|
+
class LabeledPolygramExample(Scene):
|
|
264
|
+
def construct(self):
|
|
265
|
+
# Define Rings
|
|
266
|
+
ring1 = [
|
|
267
|
+
[-3.8, -2.4, 0], [-2.4, -2.5, 0], [-1.3, -1.6, 0], [-0.2, -1.7, 0],
|
|
268
|
+
[1.7, -2.5, 0], [2.9, -2.6, 0], [3.5, -1.5, 0], [4.9, -1.4, 0],
|
|
269
|
+
[4.5, 0.2, 0], [4.7, 1.6, 0], [3.5, 2.4, 0], [1.1, 2.5, 0],
|
|
270
|
+
[-0.1, 0.9, 0], [-1.2, 0.5, 0], [-1.6, 0.7, 0], [-1.4, 1.9, 0],
|
|
271
|
+
[-2.6, 2.6, 0], [-4.4, 1.2, 0], [-4.9, -0.8, 0], [-3.8, -2.4, 0]
|
|
272
|
+
]
|
|
273
|
+
ring2 = [
|
|
274
|
+
[0.2, -1.2, 0], [0.9, -1.2, 0], [1.4, -2.0, 0], [2.1, -1.6, 0],
|
|
275
|
+
[2.2, -0.5, 0], [1.4, 0.0, 0], [0.4, -0.2, 0], [0.2, -1.2, 0]
|
|
276
|
+
]
|
|
277
|
+
ring3 = [[-2.7, 1.4, 0], [-2.3, 1.7, 0], [-2.8, 1.9, 0], [-2.7, 1.4, 0]]
|
|
278
|
+
|
|
279
|
+
# Create Polygons (for reference)
|
|
280
|
+
p1 = Polygon(*ring1, fill_opacity=0.75)
|
|
281
|
+
p2 = Polygon(*ring2, fill_color=BLACK, fill_opacity=1)
|
|
282
|
+
p3 = Polygon(*ring3, fill_color=BLACK, fill_opacity=1)
|
|
283
|
+
|
|
284
|
+
# Create Labeled Polygram
|
|
285
|
+
polygram = LabeledPolygram(
|
|
286
|
+
*[ring1, ring2, ring3],
|
|
287
|
+
label=Text('Pole', font='sans-serif'),
|
|
288
|
+
precision=0.01,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Display Circle (for reference)
|
|
292
|
+
circle = Circle(radius=polygram.radius, color=WHITE).move_to(polygram.pole)
|
|
293
|
+
|
|
294
|
+
self.add(p1, p2, p3)
|
|
295
|
+
self.add(polygram)
|
|
296
|
+
self.add(circle)
|
|
297
|
+
|
|
298
|
+
.. manim:: LabeledCountryExample
|
|
299
|
+
:save_last_frame:
|
|
300
|
+
:quality: high
|
|
301
|
+
|
|
302
|
+
import requests
|
|
303
|
+
import json
|
|
304
|
+
|
|
305
|
+
class LabeledCountryExample(Scene):
|
|
306
|
+
def construct(self):
|
|
307
|
+
# Fetch JSON data and process arcs
|
|
308
|
+
data = requests.get('https://cdn.jsdelivr.net/npm/us-atlas@3/nation-10m.json').json()
|
|
309
|
+
arcs, transform = data['arcs'], data['transform']
|
|
310
|
+
sarcs = [np.cumsum(arc, axis=0) * transform['scale'] + transform['translate'] for arc in arcs]
|
|
311
|
+
ssarcs = sorted(sarcs, key=len, reverse=True)[:1]
|
|
312
|
+
|
|
313
|
+
# Compute Bounding Box
|
|
314
|
+
points = np.concatenate(ssarcs)
|
|
315
|
+
mins, maxs = np.min(points, axis=0), np.max(points, axis=0)
|
|
316
|
+
|
|
317
|
+
# Build Axes
|
|
318
|
+
ax = Axes(
|
|
319
|
+
x_range=[mins[0], maxs[0], maxs[0] - mins[0]], x_length=10,
|
|
320
|
+
y_range=[mins[1], maxs[1], maxs[1] - mins[1]], y_length=7,
|
|
321
|
+
tips=False
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Adjust Coordinates
|
|
325
|
+
array = [[ax.c2p(*point) for point in sarc] for sarc in ssarcs]
|
|
326
|
+
|
|
327
|
+
# Add Polygram
|
|
328
|
+
polygram = LabeledPolygram(
|
|
329
|
+
*array,
|
|
330
|
+
label=Text('USA', font='sans-serif'),
|
|
331
|
+
precision=0.01,
|
|
332
|
+
fill_color=BLUE,
|
|
333
|
+
stroke_width=0,
|
|
334
|
+
fill_opacity=0.75
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Display Circle (for reference)
|
|
338
|
+
circle = Circle(radius=polygram.radius, color=WHITE).move_to(polygram.pole)
|
|
339
|
+
|
|
340
|
+
self.add(ax)
|
|
341
|
+
self.add(polygram)
|
|
342
|
+
self.add(circle)
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def __init__(
|
|
346
|
+
self,
|
|
347
|
+
*vertex_groups: Point3DLike_Array,
|
|
348
|
+
label: str | Tex | MathTex | Text,
|
|
349
|
+
precision: float = 0.01,
|
|
350
|
+
label_config: dict[str, Any] | None = None,
|
|
351
|
+
box_config: dict[str, Any] | None = None,
|
|
352
|
+
frame_config: dict[str, Any] | None = None,
|
|
353
|
+
**kwargs: Any,
|
|
354
|
+
) -> None:
|
|
355
|
+
# Initialize the Polygram with the vertex groups
|
|
356
|
+
super().__init__(*vertex_groups, **kwargs)
|
|
357
|
+
|
|
358
|
+
# Create Label
|
|
359
|
+
self.label = Label(
|
|
360
|
+
label=label,
|
|
361
|
+
label_config=label_config,
|
|
362
|
+
box_config=box_config,
|
|
363
|
+
frame_config=frame_config,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Close Vertex Groups
|
|
367
|
+
rings = [
|
|
368
|
+
group if np.array_equal(group[0], group[-1]) else list(group) + [group[0]]
|
|
369
|
+
for group in vertex_groups
|
|
370
|
+
]
|
|
371
|
+
|
|
372
|
+
# Compute the Pole of Inaccessibility
|
|
373
|
+
cell = polylabel(rings, precision=precision)
|
|
374
|
+
self.pole, self.radius = np.pad(cell.c, (0, 1), "constant"), cell.d
|
|
375
|
+
|
|
376
|
+
# Position the label at the pole
|
|
377
|
+
self.label.move_to(self.pole)
|
|
378
|
+
self.add(self.label)
|