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.
Files changed (163) hide show
  1. manim/__init__.py +11 -6
  2. manim/__main__.py +62 -19
  3. manim/_config/__init__.py +10 -9
  4. manim/_config/cli_colors.py +26 -9
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +23 -13
  7. manim/_config/utils.py +662 -468
  8. manim/animation/animation.py +164 -18
  9. manim/animation/changing.py +34 -23
  10. manim/animation/composition.py +265 -67
  11. manim/animation/creation.py +208 -26
  12. manim/animation/fading.py +16 -18
  13. manim/animation/growing.py +35 -15
  14. manim/animation/indication.py +150 -76
  15. manim/animation/movement.py +56 -22
  16. manim/animation/numbers.py +64 -6
  17. manim/animation/rotation.py +78 -7
  18. manim/animation/specialized.py +6 -7
  19. manim/animation/speedmodifier.py +13 -10
  20. manim/animation/transform.py +14 -11
  21. manim/animation/transform_matching_parts.py +3 -4
  22. manim/animation/updaters/mobject_update_utils.py +152 -30
  23. manim/animation/updaters/update.py +10 -7
  24. manim/camera/camera.py +182 -118
  25. manim/camera/mapping_camera.py +34 -3
  26. manim/camera/moving_camera.py +95 -74
  27. manim/camera/multi_camera.py +23 -15
  28. manim/camera/three_d_camera.py +70 -52
  29. manim/cli/__init__.py +17 -0
  30. manim/cli/cfg/group.py +76 -44
  31. manim/cli/checkhealth/checks.py +192 -0
  32. manim/cli/checkhealth/commands.py +90 -0
  33. manim/cli/default_group.py +158 -25
  34. manim/cli/init/commands.py +33 -25
  35. manim/cli/plugins/commands.py +16 -3
  36. manim/cli/render/commands.py +72 -60
  37. manim/cli/render/ease_of_access_options.py +4 -3
  38. manim/cli/render/global_options.py +59 -17
  39. manim/cli/render/output_options.py +6 -5
  40. manim/cli/render/render_options.py +98 -33
  41. manim/constants.py +109 -59
  42. manim/data_structures.py +31 -0
  43. manim/mobject/frame.py +8 -5
  44. manim/mobject/geometry/__init__.py +1 -0
  45. manim/mobject/geometry/arc.py +277 -135
  46. manim/mobject/geometry/boolean_ops.py +32 -31
  47. manim/mobject/geometry/labeled.py +376 -0
  48. manim/mobject/geometry/line.py +192 -87
  49. manim/mobject/geometry/polygram.py +224 -58
  50. manim/mobject/geometry/shape_matchers.py +61 -25
  51. manim/mobject/geometry/tips.py +122 -48
  52. manim/mobject/graph.py +1027 -419
  53. manim/mobject/graphing/coordinate_systems.py +533 -278
  54. manim/mobject/graphing/functions.py +53 -32
  55. manim/mobject/graphing/number_line.py +123 -65
  56. manim/mobject/graphing/probability.py +88 -62
  57. manim/mobject/graphing/scale.py +33 -19
  58. manim/mobject/logo.py +118 -28
  59. manim/mobject/matrix.py +87 -83
  60. manim/mobject/mobject.py +912 -442
  61. manim/mobject/opengl/dot_cloud.py +16 -5
  62. manim/mobject/opengl/opengl_compatibility.py +4 -2
  63. manim/mobject/opengl/opengl_geometry.py +254 -153
  64. manim/mobject/opengl/opengl_image_mobject.py +3 -1
  65. manim/mobject/opengl/opengl_mobject.py +779 -482
  66. manim/mobject/opengl/opengl_point_cloud_mobject.py +41 -14
  67. manim/mobject/opengl/opengl_surface.py +14 -92
  68. manim/mobject/opengl/opengl_three_dimensions.py +12 -8
  69. manim/mobject/opengl/opengl_vectorized_mobject.py +98 -100
  70. manim/mobject/svg/brace.py +173 -41
  71. manim/mobject/svg/svg_mobject.py +139 -53
  72. manim/mobject/table.py +61 -68
  73. manim/mobject/text/code_mobject.py +193 -539
  74. manim/mobject/text/numbers.py +81 -34
  75. manim/mobject/text/tex_mobject.py +130 -78
  76. manim/mobject/text/text_mobject.py +288 -164
  77. manim/mobject/three_d/polyhedra.py +111 -13
  78. manim/mobject/three_d/three_d_utils.py +17 -8
  79. manim/mobject/three_d/three_dimensions.py +239 -106
  80. manim/mobject/types/image_mobject.py +50 -30
  81. manim/mobject/types/point_cloud_mobject.py +120 -75
  82. manim/mobject/types/vectorized_mobject.py +841 -408
  83. manim/mobject/value_tracker.py +105 -38
  84. manim/mobject/vector_field.py +50 -31
  85. manim/opengl/__init__.py +3 -3
  86. manim/plugins/__init__.py +14 -1
  87. manim/plugins/plugins_flags.py +10 -14
  88. manim/renderer/cairo_renderer.py +65 -50
  89. manim/renderer/opengl_renderer.py +89 -69
  90. manim/renderer/opengl_renderer_window.py +39 -18
  91. manim/renderer/shader.py +123 -87
  92. manim/renderer/shader_wrapper.py +44 -28
  93. manim/renderer/vectorized_mobject_rendering.py +38 -10
  94. manim/scene/moving_camera_scene.py +32 -3
  95. manim/scene/scene.py +507 -242
  96. manim/scene/scene_file_writer.py +371 -220
  97. manim/scene/section.py +20 -16
  98. manim/scene/three_d_scene.py +14 -22
  99. manim/scene/vector_space_scene.py +223 -129
  100. manim/scene/zoomed_scene.py +46 -41
  101. manim/typing.py +990 -0
  102. manim/utils/bezier.py +1823 -371
  103. manim/utils/caching.py +12 -5
  104. manim/utils/color/AS2700.py +236 -0
  105. manim/utils/color/BS381.py +318 -0
  106. manim/utils/color/DVIPSNAMES.py +96 -0
  107. manim/utils/color/SVGNAMES.py +179 -0
  108. manim/utils/color/X11.py +533 -0
  109. manim/utils/color/XKCD.py +952 -0
  110. manim/utils/color/__init__.py +61 -0
  111. manim/utils/color/core.py +1667 -0
  112. manim/utils/color/manim_colors.py +218 -0
  113. manim/utils/commands.py +48 -20
  114. manim/utils/config_ops.py +39 -19
  115. manim/utils/debug.py +8 -7
  116. manim/utils/deprecation.py +86 -39
  117. manim/utils/docbuild/__init__.py +17 -0
  118. manim/utils/docbuild/autoaliasattr_directive.py +236 -0
  119. manim/utils/docbuild/autocolor_directive.py +99 -0
  120. manim/utils/docbuild/manim_directive.py +94 -41
  121. manim/utils/docbuild/module_parsing.py +245 -0
  122. manim/utils/exceptions.py +6 -0
  123. manim/utils/family.py +5 -3
  124. manim/utils/family_ops.py +17 -4
  125. manim/utils/file_ops.py +27 -17
  126. manim/utils/hashing.py +55 -45
  127. manim/utils/images.py +13 -7
  128. manim/utils/ipython_magic.py +13 -7
  129. manim/utils/iterables.py +163 -120
  130. manim/utils/module_ops.py +66 -24
  131. manim/utils/opengl.py +77 -24
  132. manim/utils/parameter_parsing.py +32 -0
  133. manim/utils/paths.py +30 -33
  134. manim/utils/polylabel.py +235 -0
  135. manim/utils/qhull.py +218 -0
  136. manim/utils/rate_functions.py +98 -32
  137. manim/utils/simple_functions.py +25 -33
  138. manim/utils/sounds.py +7 -1
  139. manim/utils/space_ops.py +188 -115
  140. manim/utils/testing/__init__.py +17 -0
  141. manim/utils/testing/_frames_testers.py +13 -8
  142. manim/utils/testing/_show_diff.py +5 -3
  143. manim/utils/testing/_test_class_makers.py +34 -18
  144. manim/utils/testing/frames_comparison.py +37 -19
  145. manim/utils/tex.py +130 -198
  146. manim/utils/tex_file_writing.py +77 -47
  147. manim/utils/tex_templates.py +2 -1
  148. manim/utils/unit.py +6 -5
  149. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/METADATA +64 -65
  150. manim-0.19.1.dist-info/RECORD +220 -0
  151. {manim-0.17.0.dist-info → manim-0.19.1.dist-info}/WHEEL +1 -1
  152. manim-0.19.1.dist-info/entry_points.txt +3 -0
  153. {manim-0.17.0.dist-info → manim-0.19.1.dist-info/licenses}/LICENSE.community +1 -1
  154. manim/cli/new/group.py +0 -189
  155. manim/communitycolors.py +0 -9
  156. manim/gui/__init__.py +0 -0
  157. manim/gui/gui.py +0 -82
  158. manim/plugins/import_plugins.py +0 -43
  159. manim/utils/color.py +0 -552
  160. manim-0.17.0.dist-info/RECORD +0 -206
  161. manim-0.17.0.dist-info/entry_points.txt +0 -4
  162. /manim/cli/{new → checkhealth}/__init__.py +0 -0
  163. {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 typing
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: typing.Iterable,
31
+ points: Point2DLike_Array | Point3DLike_Array,
32
32
  z_dim: float = 0.0,
33
- ) -> list[np.ndarray]:
34
- """Converts an iterable with coordinates in 2d to 3d by adding
35
- :attr:`z_dim` as the z coordinate.
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 which has the coordinates.
41
- z_dim:
42
- The default value of z coordinate.
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
- typing.List[np.ndarray]
47
- A list of array converted to 3d.
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
- [array([1., 2., 0.]), array([3., 4., 0.])]
54
+ array([[1., 2., 0.],
55
+ [3., 4., 0.]])
55
56
  """
56
- points = list(points)
57
- for i, point in enumerate(points):
57
+ list_of_points = list(points)
58
+ for i, point in enumerate(list_of_points):
58
59
  if len(point) == 2:
59
- points[i] = np.array(list(point) + [z_dim])
60
- return points
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 not np.all(np.isfinite(vmobject.points)):
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 p0, p1, p2 in quads:
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 p0, p1, p2, p3 in quads:
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: %s" % path_verb)
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)