manim 0.18.0.post0__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.

Files changed (146) hide show
  1. manim/__init__.py +3 -6
  2. manim/__main__.py +61 -20
  3. manim/_config/__init__.py +6 -3
  4. manim/_config/cli_colors.py +16 -8
  5. manim/_config/default.cfg +1 -3
  6. manim/_config/logger_utils.py +14 -8
  7. manim/_config/utils.py +651 -472
  8. manim/animation/animation.py +152 -5
  9. manim/animation/composition.py +80 -39
  10. manim/animation/creation.py +196 -14
  11. manim/animation/fading.py +5 -9
  12. manim/animation/indication.py +103 -47
  13. manim/animation/movement.py +22 -5
  14. manim/animation/rotation.py +3 -2
  15. manim/animation/specialized.py +4 -6
  16. manim/animation/speedmodifier.py +10 -5
  17. manim/animation/transform.py +4 -5
  18. manim/animation/transform_matching_parts.py +1 -1
  19. manim/animation/updaters/mobject_update_utils.py +17 -14
  20. manim/camera/camera.py +15 -6
  21. manim/cli/__init__.py +17 -0
  22. manim/cli/cfg/group.py +70 -44
  23. manim/cli/checkhealth/checks.py +93 -75
  24. manim/cli/checkhealth/commands.py +14 -5
  25. manim/cli/default_group.py +157 -25
  26. manim/cli/init/commands.py +32 -24
  27. manim/cli/plugins/commands.py +16 -3
  28. manim/cli/render/commands.py +72 -60
  29. manim/cli/render/ease_of_access_options.py +4 -3
  30. manim/cli/render/global_options.py +51 -15
  31. manim/cli/render/output_options.py +6 -5
  32. manim/cli/render/render_options.py +97 -32
  33. manim/constants.py +65 -19
  34. manim/gui/gui.py +2 -0
  35. manim/mobject/frame.py +0 -1
  36. manim/mobject/geometry/arc.py +112 -78
  37. manim/mobject/geometry/boolean_ops.py +32 -25
  38. manim/mobject/geometry/labeled.py +300 -77
  39. manim/mobject/geometry/line.py +132 -64
  40. manim/mobject/geometry/polygram.py +126 -30
  41. manim/mobject/geometry/shape_matchers.py +35 -15
  42. manim/mobject/geometry/tips.py +38 -29
  43. manim/mobject/graph.py +414 -133
  44. manim/mobject/graphing/coordinate_systems.py +126 -64
  45. manim/mobject/graphing/functions.py +25 -15
  46. manim/mobject/graphing/number_line.py +24 -10
  47. manim/mobject/graphing/probability.py +2 -10
  48. manim/mobject/graphing/scale.py +6 -5
  49. manim/mobject/matrix.py +17 -19
  50. manim/mobject/mobject.py +314 -165
  51. manim/mobject/opengl/opengl_compatibility.py +2 -0
  52. manim/mobject/opengl/opengl_geometry.py +30 -9
  53. manim/mobject/opengl/opengl_image_mobject.py +2 -0
  54. manim/mobject/opengl/opengl_mobject.py +509 -343
  55. manim/mobject/opengl/opengl_point_cloud_mobject.py +5 -7
  56. manim/mobject/opengl/opengl_surface.py +3 -2
  57. manim/mobject/opengl/opengl_three_dimensions.py +2 -0
  58. manim/mobject/opengl/opengl_vectorized_mobject.py +46 -79
  59. manim/mobject/svg/brace.py +63 -13
  60. manim/mobject/svg/svg_mobject.py +4 -3
  61. manim/mobject/table.py +11 -13
  62. manim/mobject/text/code_mobject.py +186 -548
  63. manim/mobject/text/numbers.py +9 -7
  64. manim/mobject/text/tex_mobject.py +23 -14
  65. manim/mobject/text/text_mobject.py +70 -24
  66. manim/mobject/three_d/polyhedra.py +98 -1
  67. manim/mobject/three_d/three_d_utils.py +4 -4
  68. manim/mobject/three_d/three_dimensions.py +62 -34
  69. manim/mobject/types/image_mobject.py +42 -24
  70. manim/mobject/types/point_cloud_mobject.py +105 -67
  71. manim/mobject/types/vectorized_mobject.py +496 -228
  72. manim/mobject/value_tracker.py +5 -4
  73. manim/mobject/vector_field.py +5 -5
  74. manim/opengl/__init__.py +3 -3
  75. manim/plugins/__init__.py +14 -1
  76. manim/plugins/plugins_flags.py +14 -8
  77. manim/renderer/cairo_renderer.py +20 -10
  78. manim/renderer/opengl_renderer.py +21 -23
  79. manim/renderer/opengl_renderer_window.py +2 -0
  80. manim/renderer/shader.py +2 -3
  81. manim/renderer/shader_wrapper.py +5 -2
  82. manim/renderer/vectorized_mobject_rendering.py +5 -0
  83. manim/scene/moving_camera_scene.py +23 -0
  84. manim/scene/scene.py +90 -43
  85. manim/scene/scene_file_writer.py +316 -165
  86. manim/scene/section.py +17 -15
  87. manim/scene/three_d_scene.py +13 -21
  88. manim/scene/vector_space_scene.py +22 -9
  89. manim/typing.py +830 -70
  90. manim/utils/bezier.py +1667 -399
  91. manim/utils/caching.py +13 -5
  92. manim/utils/color/AS2700.py +2 -0
  93. manim/utils/color/BS381.py +3 -0
  94. manim/utils/color/DVIPSNAMES.py +96 -0
  95. manim/utils/color/SVGNAMES.py +179 -0
  96. manim/utils/color/X11.py +3 -0
  97. manim/utils/color/XKCD.py +3 -0
  98. manim/utils/color/__init__.py +8 -5
  99. manim/utils/color/core.py +844 -309
  100. manim/utils/color/manim_colors.py +7 -9
  101. manim/utils/commands.py +48 -20
  102. manim/utils/config_ops.py +18 -13
  103. manim/utils/debug.py +8 -7
  104. manim/utils/deprecation.py +90 -40
  105. manim/utils/docbuild/__init__.py +17 -0
  106. manim/utils/docbuild/autoaliasattr_directive.py +234 -0
  107. manim/utils/docbuild/autocolor_directive.py +21 -17
  108. manim/utils/docbuild/manim_directive.py +50 -35
  109. manim/utils/docbuild/module_parsing.py +245 -0
  110. manim/utils/exceptions.py +6 -0
  111. manim/utils/family.py +5 -3
  112. manim/utils/family_ops.py +17 -4
  113. manim/utils/file_ops.py +26 -16
  114. manim/utils/hashing.py +9 -7
  115. manim/utils/images.py +10 -4
  116. manim/utils/ipython_magic.py +14 -8
  117. manim/utils/iterables.py +161 -119
  118. manim/utils/module_ops.py +57 -19
  119. manim/utils/opengl.py +83 -24
  120. manim/utils/parameter_parsing.py +32 -0
  121. manim/utils/paths.py +21 -23
  122. manim/utils/polylabel.py +168 -0
  123. manim/utils/qhull.py +218 -0
  124. manim/utils/rate_functions.py +74 -39
  125. manim/utils/simple_functions.py +24 -15
  126. manim/utils/sounds.py +7 -1
  127. manim/utils/space_ops.py +125 -69
  128. manim/utils/testing/__init__.py +17 -0
  129. manim/utils/testing/_frames_testers.py +13 -8
  130. manim/utils/testing/_show_diff.py +5 -3
  131. manim/utils/testing/_test_class_makers.py +33 -18
  132. manim/utils/testing/frames_comparison.py +27 -19
  133. manim/utils/tex.py +127 -197
  134. manim/utils/tex_file_writing.py +47 -45
  135. manim/utils/tex_templates.py +2 -1
  136. manim/utils/unit.py +6 -5
  137. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE.community +1 -1
  138. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/METADATA +40 -39
  139. manim-0.19.0.dist-info/RECORD +221 -0
  140. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  141. manim/cli/new/__init__.py +0 -0
  142. manim/cli/new/group.py +0 -189
  143. manim/plugins/import_plugins.py +0 -43
  144. manim-0.18.0.post0.dist-info/RECORD +0 -217
  145. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  146. {manim-0.18.0.post0.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
manim/utils/color/core.py CHANGED
@@ -1,10 +1,9 @@
1
- """Manim's (internal) color data structure and some utilities for
2
- color conversion.
1
+ """Manim's (internal) color data structure and some utilities for color conversion.
3
2
 
4
- This module contains the implementation of :class:`.ManimColor`,
5
- the data structure internally used to represent colors.
3
+ This module contains the implementation of :class:`.ManimColor`, the data structure
4
+ internally used to represent colors.
6
5
 
7
- The preferred way of using these colors is by importing their constants from manim:
6
+ The preferred way of using these colors is by importing their constants from Manim:
8
7
 
9
8
  .. code-block:: pycon
10
9
 
@@ -12,12 +11,54 @@ The preferred way of using these colors is by importing their constants from man
12
11
  >>> print(RED)
13
12
  #FC6255
14
13
 
15
- Note this way uses the name of the colors in UPPERCASE.
14
+ Note that this way uses the name of the colors in UPPERCASE.
16
15
 
17
16
  .. note::
18
17
 
19
- The colors of type "C" have an alias equal to the colorname without a letter,
20
- e.g. GREEN = GREEN_C
18
+ The colors with a ``_C`` suffix have an alias equal to the colorname without a
19
+ letter. For example, ``GREEN = GREEN_C``.
20
+
21
+ ===================
22
+ Custom Color Spaces
23
+ ===================
24
+
25
+ Hello, dear visitor. You seem to be interested in implementing a custom color class for
26
+ a color space we don't currently support.
27
+
28
+ The current system is using a few indirections for ensuring a consistent behavior with
29
+ all other color types in Manim.
30
+
31
+ To implement a custom color space, you must subclass :class:`ManimColor` and implement
32
+ three important methods:
33
+
34
+ - :attr:`~.ManimColor._internal_value`: a ``@property`` implemented on
35
+ :class:`ManimColor` with the goal of keeping a consistent internal representation
36
+ which can be referenced by other functions in :class:`ManimColor`. This property acts
37
+ as a proxy to whatever representation you need in your class.
38
+
39
+ - The getter should always return a NumPy array in the format ``[r,g,b,a]``, in
40
+ accordance with the type :class:`ManimColorInternal`.
41
+
42
+ - The setter should always accept a value in the format ``[r,g,b,a]`` which can be
43
+ converted to whatever attributes you need.
44
+
45
+ - :attr:`~ManimColor._internal_space`: a read-only ``@property`` implemented on
46
+ :class:`ManimColor` with the goal of providing a useful representation which can be
47
+ used by operators, interpolation and color transform functions.
48
+
49
+ The only constraints on this value are:
50
+
51
+ - It must be a NumPy array.
52
+
53
+ - The last value must be the opacity in a range ``0.0`` to ``1.0``.
54
+
55
+ Additionally, your ``__init__`` must support this format as an initialization value
56
+ without additional parameters to ensure correct functionality of all other methods in
57
+ :class:`ManimColor`.
58
+
59
+ - :meth:`~ManimColor._from_internal`: a ``@classmethod`` which converts an
60
+ ``[r,g,b,a]`` value into suitable parameters for your ``__init__`` method and calls
61
+ the ``cls`` parameter.
21
62
  """
22
63
 
23
64
  from __future__ import annotations
@@ -27,17 +68,24 @@ import colorsys
27
68
  # logger = _config.logger
28
69
  import random
29
70
  import re
30
- from typing import Any, Sequence, TypeVar, Union, overload
71
+ from collections.abc import Sequence
72
+ from typing import TypeVar, Union, overload
31
73
 
32
74
  import numpy as np
33
75
  import numpy.typing as npt
34
- from typing_extensions import Self, TypeAlias
76
+ from typing_extensions import Self, TypeAlias, TypeIs, override
35
77
 
36
78
  from manim.typing import (
79
+ HSL_Array_Float,
80
+ HSL_Tuple_Float,
37
81
  HSV_Array_Float,
38
82
  HSV_Tuple_Float,
83
+ HSVA_Array_Float,
84
+ HSVA_Tuple_Float,
39
85
  ManimColorDType,
40
86
  ManimColorInternal,
87
+ ManimFloat,
88
+ Point3D,
41
89
  RGB_Array_Float,
42
90
  RGB_Array_Int,
43
91
  RGB_Tuple_Float,
@@ -46,48 +94,66 @@ from manim.typing import (
46
94
  RGBA_Array_Int,
47
95
  RGBA_Tuple_Float,
48
96
  RGBA_Tuple_Int,
97
+ Vector3D,
49
98
  )
50
99
 
51
100
  from ...utils.space_ops import normalize
52
101
 
53
102
  # import manim._config as _config
54
103
 
55
-
56
- re_hex = re.compile("((?<=#)|(?<=0x))[A-F0-9]{6,8}", re.IGNORECASE)
104
+ re_hex = re.compile("((?<=#)|(?<=0x))[A-F0-9]{3,8}", re.IGNORECASE)
57
105
 
58
106
 
59
107
  class ManimColor:
60
108
  """Internal representation of a color.
61
109
 
62
- The ManimColor class is the main class for the representation of a color.
63
- It's internal representation is a 4 element array of floats corresponding
64
- to a [r,g,b,a] value where r,g,b,a can be between 0 to 1.
110
+ The :class:`ManimColor` class is the main class for the representation of a color.
111
+ Its internal representation is an array of 4 floats corresponding to a ``[r,g,b,a]``
112
+ value where ``r,g,b,a`` can be between 0.0 and 1.0.
65
113
 
66
- This is done in order to reduce the amount of color inconsitencies by constantly
114
+ This is done in order to reduce the amount of color inconsistencies by constantly
67
115
  casting between integers and floats which introduces errors.
68
116
 
69
117
  The class can accept any value of type :class:`ParsableManimColor` i.e.
70
118
 
71
- ManimColor, int, str, RGB_Tuple_Int, RGB_Tuple_Float, RGBA_Tuple_Int, RGBA_Tuple_Float, RGB_Array_Int,
72
- RGB_Array_Float, RGBA_Array_Int, RGBA_Array_Float
119
+ ``ManimColor, int, str, RGB_Tuple_Int, RGB_Tuple_Float, RGBA_Tuple_Int, RGBA_Tuple_Float, RGB_Array_Int,
120
+ RGB_Array_Float, RGBA_Array_Int, RGBA_Array_Float``
73
121
 
74
- ManimColor itself only accepts singular values and will directly interpret them into a single color if possible
75
- Be careful when passing strings to ManimColor it can create a big overhead for the color processing.
122
+ :class:`ManimColor` itself only accepts singular values and will directly interpret
123
+ them into a single color if possible. Be careful when passing strings to
124
+ :class:`ManimColor`: it can create a big overhead for the color processing.
76
125
 
77
- If you want to parse a list of colors use the function :meth:`parse` in :class:`ManimColor` which assumes that
78
- you are going to pass a list of color so arrays will not bei interpreted as a single color.
126
+ If you want to parse a list of colors, use the :meth:`parse` method, which assumes
127
+ that you're going to pass a list of colors so that arrays will not be interpreted as
128
+ a single color.
79
129
 
80
130
  .. warning::
81
- If you pass an array of numbers to :meth:`parse` it will interpret the r,g,b,a numbers in that array as colors
82
- so instead of the expect singular color you get and array with 4 colors.
131
+ If you pass an array of numbers to :meth:`parse`, it will interpret the
132
+ ``r,g,b,a`` numbers in that array as colors: Instead of the expected
133
+ singular color, you will get an array with 4 colors.
134
+
135
+ For conversion behaviors, see the ``_internal`` functions for further documentation.
136
+
137
+ You can create a :class:`ManimColor` instance via its classmethods. See the
138
+ respective methods for more info.
83
139
 
84
- For conversion behaviors see the _internal functions for further documentation
140
+ .. code-block:: python
141
+
142
+ mycolor = ManimColor.from_rgb((0, 1, 0.4, 0.5))
143
+ myothercolor = ManimColor.from_rgb((153, 255, 255))
144
+
145
+ You can also convert between different color spaces:
146
+
147
+ .. code-block:: python
148
+
149
+ mycolor_hex = mycolor.to_hex()
150
+ myoriginalcolor = ManimColor.from_hex(mycolor_hex).to_hsv()
85
151
 
86
152
  Parameters
87
153
  ----------
88
154
  value
89
155
  Some representation of a color (e.g., a string or
90
- a suitable tuple).
156
+ a suitable tuple). The default ``None`` is ``BLACK``.
91
157
  alpha
92
158
  The opacity of the color. By default, colors are
93
159
  fully opaque (value 1.0).
@@ -118,14 +184,14 @@ class ManimColor:
118
184
  # This is not expected to be called on module initialization time
119
185
  # It can be horribly slow to convert a string to a color because
120
186
  # it has to access the dictionary of colors and find the right color
121
- self._internal_value = ManimColor._internal_from_string(value)
187
+ self._internal_value = ManimColor._internal_from_string(value, alpha)
122
188
  elif isinstance(value, (list, tuple, np.ndarray)):
123
189
  length = len(value)
124
190
  if all(isinstance(x, float) for x in value):
125
191
  if length == 3:
126
- self._internal_value = ManimColor._internal_from_rgb(value, alpha) # type: ignore
192
+ self._internal_value = ManimColor._internal_from_rgb(value, alpha) # type: ignore[arg-type]
127
193
  elif length == 4:
128
- self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore
194
+ self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore[arg-type]
129
195
  else:
130
196
  raise ValueError(
131
197
  f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
@@ -133,10 +199,11 @@ class ManimColor:
133
199
  else:
134
200
  if length == 3:
135
201
  self._internal_value = ManimColor._internal_from_int_rgb(
136
- value, alpha # type: ignore
202
+ value, # type: ignore[arg-type]
203
+ alpha,
137
204
  )
138
205
  elif length == 4:
139
- self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore
206
+ self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore[arg-type]
140
207
  else:
141
208
  raise ValueError(
142
209
  f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
@@ -145,7 +212,6 @@ class ManimColor:
145
212
  result = re_hex.search(value.get_hex())
146
213
  if result is None:
147
214
  raise ValueError(f"Failed to parse a color from {value}")
148
-
149
215
  self._internal_value = ManimColor._internal_from_hex_string(
150
216
  result.group(), alpha
151
217
  )
@@ -157,37 +223,59 @@ class ManimColor:
157
223
  f"list[float, float, float, float], not {type(value)}"
158
224
  )
159
225
 
226
+ @property
227
+ def _internal_space(self) -> npt.NDArray[ManimFloat]:
228
+ """This is a readonly property which is a custom representation for color space
229
+ operations. It is used for operators and can be used when implementing a custom
230
+ color space.
231
+ """
232
+ return self._internal_value
233
+
160
234
  @property
161
235
  def _internal_value(self) -> ManimColorInternal:
162
- """Returns the internal value of the current Manim color [r,g,b,a] float array
236
+ """Return the internal value of the current Manim color ``[r,g,b,a]`` float
237
+ array.
163
238
 
164
239
  Returns
165
240
  -------
166
241
  ManimColorInternal
167
- internal color representation
242
+ Internal color representation.
168
243
  """
169
244
  return self.__value
170
245
 
171
246
  @_internal_value.setter
172
247
  def _internal_value(self, value: ManimColorInternal) -> None:
173
- """Overwrites the internal color value of the ManimColor object
248
+ """Overwrite the internal color value of this :class:`ManimColor`.
174
249
 
175
250
  Parameters
176
251
  ----------
177
- value : ManimColorInternal
178
- The value which will overwrite the current color
252
+ value
253
+ The value which will overwrite the current color.
179
254
 
180
255
  Raises
181
256
  ------
182
257
  TypeError
183
- Raises a TypeError if an invalid array is passed
258
+ If an invalid array is passed.
184
259
  """
185
260
  if not isinstance(value, np.ndarray):
186
- raise TypeError("value must be a numpy array")
261
+ raise TypeError("Value must be a NumPy array.")
187
262
  if value.shape[0] != 4:
188
- raise TypeError("Array must have 4 values exactly")
263
+ raise TypeError("Array must have exactly 4 values.")
189
264
  self.__value: ManimColorInternal = value
190
265
 
266
+ @classmethod
267
+ def _construct_from_space(
268
+ cls,
269
+ _space: npt.NDArray[ManimFloat]
270
+ | tuple[float, float, float]
271
+ | tuple[float, float, float, float],
272
+ ) -> Self:
273
+ """This function is used as a proxy for constructing a color with an internal
274
+ value. This can be used by subclasses to hook into the construction of new
275
+ objects using the internal value format.
276
+ """
277
+ return cls(_space)
278
+
191
279
  @staticmethod
192
280
  def _internal_from_integer(value: int, alpha: float) -> ManimColorInternal:
193
281
  return np.asarray(
@@ -200,32 +288,41 @@ class ManimColor:
200
288
  dtype=ManimColorDType,
201
289
  )
202
290
 
203
- # TODO: Maybe make 8 nibble hex also convertible ?
204
291
  @staticmethod
205
- def _internal_from_hex_string(hex: str, alpha: float) -> ManimColorInternal:
206
- """Internal function for converting a hex string into the internal representation of a ManimColor.
292
+ def _internal_from_hex_string(hex_: str, alpha: float) -> ManimColorInternal:
293
+ """Internal function for converting a hex string into the internal representation
294
+ of a :class:`ManimColor`.
207
295
 
208
296
  .. warning::
209
297
  This does not accept any prefixes like # or similar in front of the hex string.
210
- This is just intended for the raw hex part
298
+ This is just intended for the raw hex part.
211
299
 
212
300
  *For internal use only*
213
301
 
214
302
  Parameters
215
303
  ----------
216
- hex : str
217
- hex string to be parsed
218
- alpha : float
219
- alpha value used for the color
304
+ hex
305
+ Hex string to be parsed.
306
+ alpha
307
+ Alpha value used for the color, if the color is only 3 bytes long. Otherwise,
308
+ if the color is 4 bytes long, this parameter will not be used.
220
309
 
221
310
  Returns
222
311
  -------
223
312
  ManimColorInternal
224
313
  Internal color representation
225
314
  """
226
- if len(hex) == 6:
227
- hex += "00"
228
- tmp = int(hex, 16)
315
+ if len(hex_) in (3, 4):
316
+ hex_ = "".join([x * 2 for x in hex_])
317
+ if len(hex_) == 6:
318
+ hex_ += "FF"
319
+ elif len(hex_) == 8:
320
+ alpha = (int(hex_, 16) & 0xFF) / 255
321
+ else:
322
+ raise ValueError(
323
+ "Hex colors must be specified with either 0x or # as prefix and contain 6 or 8 hexadecimal numbers"
324
+ )
325
+ tmp = int(hex_, 16)
229
326
  return np.asarray(
230
327
  (
231
328
  ((tmp >> 24) & 0xFF) / 255,
@@ -240,22 +337,22 @@ class ManimColor:
240
337
  def _internal_from_int_rgb(
241
338
  rgb: RGB_Tuple_Int, alpha: float = 1.0
242
339
  ) -> ManimColorInternal:
243
- """Internal function for converting a rgb tuple of integers into the internal representation of a ManimColor.
340
+ """Internal function for converting an RGB tuple of integers into the internal
341
+ representation of a :class:`ManimColor`.
244
342
 
245
343
  *For internal use only*
246
344
 
247
345
  Parameters
248
346
  ----------
249
- rgb : RGB_Tuple_Int
250
- integer rgb tuple to be parsed
251
- alpha : float, optional
252
- optional alpha value, by default 1.0
347
+ rgb
348
+ Integer RGB tuple to be parsed
349
+ alpha
350
+ Optional alpha value. Default is 1.0.
253
351
 
254
352
  Returns
255
353
  -------
256
354
  ManimColorInternal
257
- Internal color representation
258
-
355
+ Internal color representation.
259
356
  """
260
357
  value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy() / 255
261
358
  value.resize(4, refcheck=False)
@@ -266,22 +363,22 @@ class ManimColor:
266
363
  def _internal_from_rgb(
267
364
  rgb: RGB_Tuple_Float, alpha: float = 1.0
268
365
  ) -> ManimColorInternal:
269
- """Internal function for converting a rgb tuple of floats into the internal representation of a ManimColor.
366
+ """Internal function for converting a rgb tuple of floats into the internal
367
+ representation of a :class:`ManimColor`.
270
368
 
271
369
  *For internal use only*
272
370
 
273
371
  Parameters
274
372
  ----------
275
- rgb : RGB_Tuple_Float
276
- float rgb tuple to be parsed
277
-
278
- alpha : float, optional
279
- optional alpha value, by default 1.0
373
+ rgb
374
+ Float RGB tuple to be parsed.
375
+ alpha
376
+ Optional alpha value. Default is 1.0.
280
377
 
281
378
  Returns
282
379
  -------
283
380
  ManimColorInternal
284
- Internal color representation
381
+ Internal color representation.
285
382
  """
286
383
  value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy()
287
384
  value.resize(4, refcheck=False)
@@ -290,324 +387,527 @@ class ManimColor:
290
387
 
291
388
  @staticmethod
292
389
  def _internal_from_int_rgba(rgba: RGBA_Tuple_Int) -> ManimColorInternal:
293
- """Internal function for converting a rgba tuple of integers into the internal representation of a ManimColor.
390
+ """Internal function for converting an RGBA tuple of integers into the internal
391
+ representation of a :class:`ManimColor`.
294
392
 
295
393
  *For internal use only*
296
394
 
297
395
  Parameters
298
396
  ----------
299
- rgba : RGBA_Tuple_Int
300
- int rgba tuple to be parsed
397
+ rgba
398
+ Int RGBA tuple to be parsed.
301
399
 
302
400
  Returns
303
401
  -------
304
402
  ManimColorInternal
305
- Internal color representation
403
+ Internal color representation.
306
404
  """
307
405
  return np.asarray(rgba, dtype=ManimColorDType) / 255
308
406
 
309
407
  @staticmethod
310
408
  def _internal_from_rgba(rgba: RGBA_Tuple_Float) -> ManimColorInternal:
311
- """Internal function for converting a rgba tuple of floats into the internal representation of a ManimColor.
409
+ """Internal function for converting an RGBA tuple of floats into the internal
410
+ representation of a :class:`ManimColor`.
312
411
 
313
412
  *For internal use only*
314
413
 
315
414
  Parameters
316
415
  ----------
317
- rgba : RGBA_Tuple_Float
318
- int rgba tuple to be parsed
416
+ rgba
417
+ Int RGBA tuple to be parsed.
319
418
 
320
419
  Returns
321
420
  -------
322
421
  ManimColorInternal
323
- Internal color representation
422
+ Internal color representation.
324
423
  """
325
424
  return np.asarray(rgba, dtype=ManimColorDType)
326
425
 
327
426
  @staticmethod
328
- def _internal_from_string(name: str) -> ManimColorInternal:
329
- """Internal function for converting a string into the internal representation of a ManimColor.
330
- This is not used for hex strings, please refer to :meth:`_internal_from_hex` for this functionality.
427
+ def _internal_from_string(name: str, alpha: float) -> ManimColorInternal:
428
+ """Internal function for converting a string into the internal representation of
429
+ a :class:`ManimColor`. This is not used for hex strings: please refer to
430
+ :meth:`_internal_from_hex` for this functionality.
331
431
 
332
432
  *For internal use only*
333
433
 
334
434
  Parameters
335
435
  ----------
336
- name : str
337
- The color name to be parsed into a color. Refer to the different color Modules in the documentation Page to
338
- find the corresponding Color names.
436
+ name
437
+ The color name to be parsed into a color. Refer to the different color
438
+ modules in the documentation page to find the corresponding color names.
339
439
 
340
440
  Returns
341
441
  -------
342
442
  ManimColorInternal
343
- Internal color representation
443
+ Internal color representation.
344
444
 
345
445
  Raises
346
446
  ------
347
447
  ValueError
348
- Raises a ValueError if the color name is not present with manim
448
+ If the color name is not present in Manim.
349
449
  """
350
450
  from . import _all_color_dict
351
451
 
352
- upper_name = name.upper()
353
-
354
- if upper_name in _all_color_dict:
355
- return _all_color_dict[upper_name]._internal_value
452
+ if tmp := _all_color_dict.get(name.upper()):
453
+ tmp._internal_value[3] = alpha
454
+ return tmp._internal_value.copy()
356
455
  else:
357
456
  raise ValueError(f"Color {name} not found")
358
457
 
359
458
  def to_integer(self) -> int:
360
- """Converts the current ManimColor into an integer
459
+ """Convert the current :class:`ManimColor` into an integer.
460
+
461
+ .. warning::
462
+ This will return only the RGB part of the color.
361
463
 
362
464
  Returns
363
465
  -------
364
466
  int
365
- integer representation of the color
366
-
367
- .. warning::
368
- This will return only the rgb part of the color
467
+ Integer representation of the color.
369
468
  """
370
- return int.from_bytes(
371
- (self._internal_value[:3] * 255).astype(int).tobytes(), "big"
372
- )
469
+ tmp = (self._internal_value[:3] * 255).astype(dtype=np.byte).tobytes()
470
+ return int.from_bytes(tmp, "big")
373
471
 
374
472
  def to_rgb(self) -> RGB_Array_Float:
375
- """Converts the current ManimColor into a rgb array of floats
473
+ """Convert the current :class:`ManimColor` into an RGB array of floats.
376
474
 
377
475
  Returns
378
476
  -------
379
477
  RGB_Array_Float
380
- rgb array with 3 elements of type float
478
+ RGB array of 3 floats from 0.0 to 1.0.
381
479
  """
382
480
  return self._internal_value[:3]
383
481
 
384
482
  def to_int_rgb(self) -> RGB_Array_Int:
385
- """Converts the current ManimColor into a rgb array of int
483
+ """Convert the current :class:`ManimColor` into an RGB array of integers.
386
484
 
387
485
  Returns
388
486
  -------
389
487
  RGB_Array_Int
390
- rgb array with 3 elements of type int
488
+ RGB array of 3 integers from 0 to 255.
391
489
  """
392
490
  return (self._internal_value[:3] * 255).astype(int)
393
491
 
394
492
  def to_rgba(self) -> RGBA_Array_Float:
395
- """Converts the current ManimColor into a rgba array of floats
493
+ """Convert the current :class:`ManimColor` into an RGBA array of floats.
396
494
 
397
495
  Returns
398
496
  -------
399
497
  RGBA_Array_Float
400
- rgba array with 4 elements of type float
498
+ RGBA array of 4 floats from 0.0 to 1.0.
401
499
  """
402
500
  return self._internal_value
403
501
 
404
502
  def to_int_rgba(self) -> RGBA_Array_Int:
405
- """Converts the current ManimColor into a rgba array of int
503
+ """Convert the current ManimColor into an RGBA array of integers.
406
504
 
407
505
 
408
506
  Returns
409
507
  -------
410
508
  RGBA_Array_Int
411
- rgba array with 4 elements of type int
509
+ RGBA array of 4 integers from 0 to 255.
412
510
  """
413
511
  return (self._internal_value * 255).astype(int)
414
512
 
415
513
  def to_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Float:
416
- """Converts the current ManimColor into a rgba array of float as :meth:`to_rgba` but you can change the alpha
417
- value.
514
+ """Convert the current :class:`ManimColor` into an RGBA array of floats. This is
515
+ similar to :meth:`to_rgba`, but you can change the alpha value.
418
516
 
419
517
  Parameters
420
518
  ----------
421
- alpha : float
422
- alpha value to be used in the return value
519
+ alpha
520
+ Alpha value to be used in the return value.
423
521
 
424
522
  Returns
425
523
  -------
426
524
  RGBA_Array_Float
427
- rgba array with 4 elements of type float
525
+ RGBA array of 4 floats from 0.0 to 1.0.
428
526
  """
429
527
  return np.fromiter((*self._internal_value[:3], alpha), dtype=ManimColorDType)
430
528
 
431
529
  def to_int_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Int:
432
- """Converts the current ManimColor into a rgba array of integers as :meth:`to_int_rgba` but you can change the alpha
433
- value.
530
+ """Convert the current :class:`ManimColor` into an RGBA array of integers. This
531
+ is similar to :meth:`to_int_rgba`, but you can change the alpha value.
434
532
 
435
533
  Parameters
436
534
  ----------
437
- alpha : float
438
- alpha value to be used for the return value. (Will automatically be scaled from 0-1 to 0-255 so just pass 0-1)
535
+ alpha
536
+ Alpha value to be used for the return value. Pass a float between 0.0 and
537
+ 1.0: it will automatically be scaled to an integer between 0 and 255.
439
538
 
440
539
  Returns
441
540
  -------
442
541
  RGBA_Array_Int
443
- rgba array with 4 elements of type int
542
+ RGBA array of 4 integers from 0 to 255.
444
543
  """
445
544
  tmp = self._internal_value * 255
446
545
  tmp[3] = alpha * 255
447
546
  return tmp.astype(int)
448
547
 
449
548
  def to_hex(self, with_alpha: bool = False) -> str:
450
- """Converts the manim color to a hexadecimal representation of the color
549
+ """Convert the :class:`ManimColor` to a hexadecimal representation of the color.
451
550
 
452
551
  Parameters
453
552
  ----------
454
- with_alpha : bool, optional
455
- Changes the result from 6 to 8 values where the last 2 nibbles represent the alpha value of 0-255,
456
- by default False
553
+ with_alpha
554
+ If ``True``, append 2 extra characters to the hex string which represent the
555
+ alpha value of the color between 0 and 255. Default is ``False``.
457
556
 
458
557
  Returns
459
558
  -------
460
559
  str
461
- A hex string starting with a # with either 6 or 8 nibbles depending on your input, by default 6 i.e #XXXXXX
560
+ A hex string starting with a ``#``, with either 6 or 8 nibbles depending on
561
+ the ``with_alpha`` parameter. By default, it has 6 nibbles, i.e. ``#XXXXXX``.
462
562
  """
463
- tmp = f"#{int(self._internal_value[0]*255):02X}{int(self._internal_value[1]*255):02X}{int(self._internal_value[2]*255):02X}"
563
+ tmp = (
564
+ f"#{int(self._internal_value[0] * 255):02X}"
565
+ f"{int(self._internal_value[1] * 255):02X}"
566
+ f"{int(self._internal_value[2] * 255):02X}"
567
+ )
464
568
  if with_alpha:
465
- tmp += f"{int(self._internal_value[3]*255):02X}"
569
+ tmp += f"{int(self._internal_value[3] * 255):02X}"
466
570
  return tmp
467
571
 
468
572
  def to_hsv(self) -> HSV_Array_Float:
469
- """Converts the Manim Color to HSV array.
573
+ """Convert the :class:`ManimColor` to an HSV array.
470
574
 
471
575
  .. note::
472
- Be careful this returns an array in the form `[h, s, v]` where the elements are floats.
473
- This might be confusing because rgb can also be an array of floats so you might want to annotate the usage
474
- of this function in your code by typing the variables with :class:`HSV_Array_Float` in order to differentiate
475
- between rgb arrays and hsv arrays
576
+ Be careful: this returns an array in the form ``[h, s, v]``, where the
577
+ elements are floats. This might be confusing, because RGB can also be an array
578
+ of floats. You might want to annotate the usage of this function in your code
579
+ by typing your HSV array variables as :class:`HSV_Array_Float` in order to
580
+ differentiate them from RGB arrays.
476
581
 
477
582
  Returns
478
583
  -------
479
584
  HSV_Array_Float
480
- A hsv array containing 3 elements of type float ranging from 0 to 1
585
+ An HSV array of 3 floats from 0.0 to 1.0.
481
586
  """
482
- return colorsys.rgb_to_hsv(*self.to_rgb())
587
+ return np.array(colorsys.rgb_to_hsv(*self.to_rgb()))
483
588
 
484
- def invert(self, with_alpha=False) -> ManimColor:
485
- """Returns an linearly inverted version of the color (no inplace changes)
589
+ def to_hsl(self) -> HSL_Array_Float:
590
+ """Convert the :class:`ManimColor` to an HSL array.
591
+
592
+ .. note::
593
+ Be careful: this returns an array in the form ``[h, s, l]``, where the
594
+ elements are floats. This might be confusing, because RGB can also be an array
595
+ of floats. You might want to annotate the usage of this function in your code
596
+ by typing your HSL array variables as :class:`HSL_Array_Float` in order to
597
+ differentiate them from RGB arrays.
598
+
599
+ Returns
600
+ -------
601
+ HSL_Array_Float
602
+ An HSL array of 3 floats from 0.0 to 1.0.
603
+ """
604
+ return np.array(colorsys.rgb_to_hls(*self.to_rgb()))
605
+
606
+ def invert(self, with_alpha: bool = False) -> Self:
607
+ """Return a new, linearly inverted version of this :class:`ManimColor` (no
608
+ inplace changes).
486
609
 
487
610
  Parameters
488
611
  ----------
489
- with_alpha : bool, optional
490
- if true the alpha value will be inverted too, by default False
612
+ with_alpha
613
+ If ``True``, the alpha value will be inverted too. Default is ``False``.
491
614
 
492
615
  .. note::
493
- This can result in unintended behavior where objects are not displayed because their alpha
494
- value is suddenly 0 or very low. Please keep that in mind when setting this to true
616
+ Setting ``with_alpha=True`` can result in unintended behavior where
617
+ objects are not displayed because their new alpha value is suddenly 0 or
618
+ very low.
495
619
 
496
620
  Returns
497
621
  -------
498
622
  ManimColor
499
- The linearly inverted ManimColor
623
+ The linearly inverted :class:`ManimColor`.
500
624
  """
501
- return ManimColor(1.0 - self._internal_value, with_alpha)
625
+ if with_alpha:
626
+ return self._construct_from_space(1.0 - self._internal_space)
627
+ else:
628
+ alpha = self._internal_space[3]
629
+ new = 1.0 - self._internal_space
630
+ new[-1] = alpha
631
+ return self._construct_from_space(new)
502
632
 
503
- def interpolate(self, other: ManimColor, alpha: float) -> ManimColor:
504
- """Interpolates between the current and the given ManimColor an returns the interpolated color
633
+ def interpolate(self, other: Self, alpha: float) -> Self:
634
+ """Interpolate between the current and the given :class:`ManimColor`, and return
635
+ the result.
505
636
 
506
637
  Parameters
507
638
  ----------
508
- other : ManimColor
509
- The other ManimColor to be used for interpolation
510
- alpha : float
511
- A point on the line in rgba colorspace connecting the two colors i.e. the interpolation point
512
-
513
- 0 corresponds to the current ManimColor and 1 corresponds to the other ManimColor
639
+ other
640
+ The other :class:`ManimColor` to be used for interpolation.
641
+ alpha
642
+ A point on the line in RGBA colorspace connecting the two colors, i.e. the
643
+ interpolation point. 0.0 corresponds to the current :class:`ManimColor` and
644
+ 1.0 corresponds to the other :class:`ManimColor`.
514
645
 
515
646
  Returns
516
647
  -------
517
648
  ManimColor
518
- The interpolated ManimColor
649
+ The interpolated :class:`ManimColor`.
519
650
  """
520
- return ManimColor(
521
- self._internal_value * (1 - alpha) + other._internal_value * alpha
651
+ return self._construct_from_space(
652
+ self._internal_space * (1 - alpha) + other._internal_space * alpha
522
653
  )
523
654
 
655
+ def darker(self, blend: float = 0.2) -> Self:
656
+ """Return a new color that is darker than the current color, i.e.
657
+ interpolated with ``BLACK``. The opacity is unchanged.
658
+
659
+ Parameters
660
+ ----------
661
+ blend
662
+ The blend ratio for the interpolation, from 0.0 (the current color
663
+ unchanged) to 1.0 (pure black). Default is 0.2, which results in a
664
+ slightly darker color.
665
+
666
+ Returns
667
+ -------
668
+ ManimColor
669
+ The darker :class:`ManimColor`.
670
+
671
+ See Also
672
+ --------
673
+ :meth:`lighter`
674
+ """
675
+ from manim.utils.color.manim_colors import BLACK
676
+
677
+ alpha = self._internal_space[3]
678
+ black = self._from_internal(BLACK._internal_value)
679
+ return self.interpolate(black, blend).opacity(alpha)
680
+
681
+ def lighter(self, blend: float = 0.2) -> Self:
682
+ """Return a new color that is lighter than the current color, i.e.
683
+ interpolated with ``WHITE``. The opacity is unchanged.
684
+
685
+ Parameters
686
+ ----------
687
+ blend
688
+ The blend ratio for the interpolation, from 0.0 (the current color
689
+ unchanged) to 1.0 (pure white). Default is 0.2, which results in a
690
+ slightly lighter color.
691
+
692
+ Returns
693
+ -------
694
+ ManimColor
695
+ The lighter :class:`ManimColor`.
696
+
697
+ See Also
698
+ --------
699
+ :meth:`darker`
700
+ """
701
+ from manim.utils.color.manim_colors import WHITE
702
+
703
+ alpha = self._internal_space[3]
704
+ white = self._from_internal(WHITE._internal_value)
705
+ return self.interpolate(white, blend).opacity(alpha)
706
+
707
+ def contrasting(
708
+ self,
709
+ threshold: float = 0.5,
710
+ light: Self | None = None,
711
+ dark: Self | None = None,
712
+ ) -> Self:
713
+ """Return one of two colors, light or dark (by default white or black),
714
+ that contrasts with the current color (depending on its luminance).
715
+ This is typically used to set text in a contrasting color that ensures
716
+ it is readable against a background of the current color.
717
+
718
+ Parameters
719
+ ----------
720
+ threshold
721
+ The luminance threshold which dictates whether the current color is
722
+ considered light or dark (and thus whether to return the dark or
723
+ light color, respectively). Default is 0.5.
724
+ light
725
+ The light color to return if the current color is considered dark.
726
+ Default is ``None``: in this case, pure ``WHITE`` will be returned.
727
+ dark
728
+ The dark color to return if the current color is considered light,
729
+ Default is ``None``: in this case, pure ``BLACK`` will be returned.
730
+
731
+ Returns
732
+ -------
733
+ ManimColor
734
+ The contrasting :class:`ManimColor`.
735
+ """
736
+ from manim.utils.color.manim_colors import BLACK, WHITE
737
+
738
+ luminance, _, _ = colorsys.rgb_to_yiq(*self.to_rgb())
739
+ if luminance < threshold:
740
+ if light is not None:
741
+ return light
742
+ return self._from_internal(WHITE._internal_value)
743
+ else:
744
+ if dark is not None:
745
+ return dark
746
+ return self._from_internal(BLACK._internal_value)
747
+
748
+ def opacity(self, opacity: float) -> Self:
749
+ """Create a new :class:`ManimColor` with the given opacity and the same color
750
+ values as before.
751
+
752
+ Parameters
753
+ ----------
754
+ opacity
755
+ The new opacity value to be used.
756
+
757
+ Returns
758
+ -------
759
+ ManimColor
760
+ The new :class:`ManimColor` with the same color values and the new opacity.
761
+ """
762
+ tmp = self._internal_space.copy()
763
+ tmp[-1] = opacity
764
+ return self._construct_from_space(tmp)
765
+
766
+ def into(self, class_type: type[ManimColorT]) -> ManimColorT:
767
+ """Convert the current color into a different colorspace given by ``class_type``,
768
+ without changing the :attr:`_internal_value`.
769
+
770
+ Parameters
771
+ ----------
772
+ class_type
773
+ The class that is used for conversion. It must be a subclass of
774
+ :class:`ManimColor` which respects the specification HSV, RGBA, ...
775
+
776
+ Returns
777
+ -------
778
+ ManimColorT
779
+ A new color object of type ``class_type`` and the same
780
+ :attr:`_internal_value` as the original color.
781
+ """
782
+ return class_type._from_internal(self._internal_value)
783
+
784
+ @classmethod
785
+ def _from_internal(cls, value: ManimColorInternal) -> Self:
786
+ """This method is intended to be overwritten by custom color space classes
787
+ which are subtypes of :class:`ManimColor`.
788
+
789
+ The method constructs a new object of the given class by transforming the value
790
+ in the internal format ``[r,g,b,a]`` into a format which the constructor of the
791
+ custom class can understand. Look at :class:`.HSV` for an example.
792
+ """
793
+ return cls(value)
794
+
524
795
  @classmethod
525
796
  def from_rgb(
526
797
  cls,
527
798
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
528
799
  alpha: float = 1.0,
529
800
  ) -> Self:
530
- """Creates a ManimColor from an RGB Array. Automagically decides which type it is int/float
801
+ """Create a ManimColor from an RGB array. Automagically decides which type it
802
+ is: ``int`` or ``float``.
531
803
 
532
804
  .. warning::
533
- Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
534
- being interpreted as if it was a float rgb array with the value 5.0 and not the integer 5
805
+ Please make sure that your elements are not floats if you want integers. A
806
+ ``5.0`` will result in the input being interpreted as if it was an RGB float
807
+ array with the value ``5.0`` and not the integer ``5``.
535
808
 
536
809
 
537
810
  Parameters
538
811
  ----------
539
- rgb : RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int
540
- Any 3 Element Iterable
541
- alpha : float, optional
542
- alpha value to be used in the color, by default 1.0
812
+ rgb
813
+ Any iterable of 3 floats or 3 integers.
814
+ alpha
815
+ Alpha value to be used in the color. Default is 1.0.
543
816
 
544
817
  Returns
545
818
  -------
546
819
  ManimColor
547
- Returns the ManimColor object
820
+ The :class:`ManimColor` which corresponds to the given ``rgb``.
548
821
  """
549
- return cls(rgb, alpha)
822
+ return cls._from_internal(ManimColor(rgb, alpha)._internal_value)
550
823
 
551
824
  @classmethod
552
825
  def from_rgba(
553
826
  cls, rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
554
827
  ) -> Self:
555
- """Creates a ManimColor from an RGBA Array. Automagically decides which type it is int/float
828
+ """Create a ManimColor from an RGBA Array. Automagically decides which type it
829
+ is: ``int`` or ``float``.
556
830
 
557
831
  .. warning::
558
- Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
559
- being interpreted as if it was a float rgb array with the value 5.0 and not the integer 5
832
+ Please make sure that your elements are not floats if you want integers. A
833
+ ``5.0`` will result in the input being interpreted as if it was a float RGB
834
+ array with the float ``5.0`` and not the integer ``5``.
560
835
 
561
836
  Parameters
562
837
  ----------
563
- rgba : RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
564
- Any 4 Element Iterable
838
+ rgba
839
+ Any iterable of 4 floats or 4 integers.
565
840
 
566
841
  Returns
567
842
  -------
568
843
  ManimColor
569
- Returns the ManimColor object
844
+ The :class:`ManimColor` corresponding to the given ``rgba``.
570
845
  """
571
846
  return cls(rgba)
572
847
 
573
848
  @classmethod
574
- def from_hex(cls, hex: str, alpha: float = 1.0) -> Self:
575
- """Creates a Manim Color from a hex string, prefixes allowed # and 0x
849
+ def from_hex(cls, hex_str: str, alpha: float = 1.0) -> Self:
850
+ """Create a :class:`ManimColor` from a hex string.
576
851
 
577
852
  Parameters
578
853
  ----------
579
- hex : str
580
- The hex string to be converted (currently only supports 6 nibbles)
581
- alpha : float, optional
582
- alpha value to be used for the hex string, by default 1.0
854
+ hex_str
855
+ The hex string to be converted. The allowed prefixes for this string are
856
+ ``#`` and ``0x``. Currently, this method only supports 6 nibbles, i.e. only
857
+ strings in the format ``#XXXXXX`` or ``0xXXXXXX``.
858
+ alpha
859
+ Alpha value to be used for the hex string. Default is 1.0.
583
860
 
584
861
  Returns
585
862
  -------
586
863
  ManimColor
587
- The ManimColor represented by the hex string
864
+ The :class:`ManimColor` represented by the hex string.
588
865
  """
589
- return cls(hex, alpha)
866
+ return cls._from_internal(ManimColor(hex_str, alpha)._internal_value)
590
867
 
591
868
  @classmethod
592
869
  def from_hsv(
593
870
  cls, hsv: HSV_Array_Float | HSV_Tuple_Float, alpha: float = 1.0
594
871
  ) -> Self:
595
- """Creates a ManimColor from an HSV Array
872
+ """Create a :class:`ManimColor` from an HSV array.
596
873
 
597
874
  Parameters
598
875
  ----------
599
- hsv : HSV_Array_Float | HSV_Tuple_Float
600
- Any 3 Element Iterable containing floats from 0-1
601
- alpha : float, optional
602
- the alpha value to be used, by default 1.0
876
+ hsv
877
+ Any iterable containing 3 floats from 0.0 to 1.0.
878
+ alpha
879
+ The alpha value to be used. Default is 1.0.
603
880
 
604
881
  Returns
605
882
  -------
606
883
  ManimColor
607
- The ManimColor with the corresponding RGB values to the HSV
884
+ The :class:`ManimColor` with the corresponding RGB values to the given HSV
885
+ array.
608
886
  """
609
887
  rgb = colorsys.hsv_to_rgb(*hsv)
610
- return cls(rgb, alpha)
888
+ return cls._from_internal(ManimColor(rgb, alpha)._internal_value)
889
+
890
+ @classmethod
891
+ def from_hsl(
892
+ cls, hsl: HSL_Array_Float | HSL_Tuple_Float, alpha: float = 1.0
893
+ ) -> Self:
894
+ """Create a :class:`ManimColor` from an HSL array.
895
+
896
+ Parameters
897
+ ----------
898
+ hsl
899
+ Any iterable containing 3 floats from 0.0 to 1.0.
900
+ alpha
901
+ The alpha value to be used. Default is 1.0.
902
+
903
+ Returns
904
+ -------
905
+ ManimColor
906
+ The :class:`ManimColor` with the corresponding RGB values to the given HSL
907
+ array.
908
+ """
909
+ rgb = colorsys.hls_to_rgb(*hsl)
910
+ return cls._from_internal(ManimColor(rgb, alpha)._internal_value)
611
911
 
612
912
  @overload
613
913
  @classmethod
@@ -615,8 +915,7 @@ class ManimColor:
615
915
  cls,
616
916
  color: ParsableManimColor | None,
617
917
  alpha: float = ...,
618
- ) -> Self:
619
- ...
918
+ ) -> Self: ...
620
919
 
621
920
  @overload
622
921
  @classmethod
@@ -624,37 +923,51 @@ class ManimColor:
624
923
  cls,
625
924
  color: Sequence[ParsableManimColor],
626
925
  alpha: float = ...,
627
- ) -> list[Self]:
628
- ...
926
+ ) -> list[Self]: ...
629
927
 
630
928
  @classmethod
631
929
  def parse(
632
930
  cls,
633
- color: ParsableManimColor | list[ParsableManimColor] | None,
931
+ color: ParsableManimColor | Sequence[ParsableManimColor] | None,
634
932
  alpha: float = 1.0,
635
933
  ) -> Self | list[Self]:
636
- """
637
- Handles the parsing of a list of colors or a single color.
934
+ """Parse one color as a :class:`ManimColor` or a sequence of colors as a list of
935
+ :class:`ManimColor`'s.
638
936
 
639
937
  Parameters
640
938
  ----------
641
939
  color
642
- The color or list of colors to parse. Note that this function can not accept rgba tuples. It will assume that you mean list[ManimColor] and will return a list of ManimColors.
940
+ The color or list of colors to parse. Note that this function can not accept
941
+ tuples: it will assume that you mean ``Sequence[ParsableManimColor]`` and will
942
+ return a ``list[ManimColor]``.
643
943
  alpha
644
- The alpha value to use if a single color is passed. or if a list of colors is passed to set the value of all colors.
944
+ The alpha (opacity) value to use for the passed color(s).
645
945
 
646
946
  Returns
647
947
  -------
648
- ManimColor
649
- Either a list of colors or a singular color depending on the input
948
+ ManimColor | list[ManimColor]
949
+ Either a list of colors or a singular color, depending on the input.
650
950
  """
651
- if isinstance(color, (list, tuple)):
652
- return [cls(c, alpha) for c in color] # type: ignore
653
- return cls(color, alpha) # type: ignore
951
+
952
+ def is_sequence(
953
+ color: ParsableManimColor | Sequence[ParsableManimColor] | None,
954
+ ) -> TypeIs[Sequence[ParsableManimColor]]:
955
+ return isinstance(color, (list, tuple))
956
+
957
+ if is_sequence(color):
958
+ return [
959
+ cls._from_internal(ManimColor(c, alpha)._internal_value) for c in color
960
+ ]
961
+ else:
962
+ return cls._from_internal(ManimColor(color, alpha)._internal_value)
654
963
 
655
964
  @staticmethod
656
- def gradient(colors: list[ManimColor], length: int):
657
- """This is not implemented by now refer to :func:`color_gradient` for a working implementation for now"""
965
+ def gradient(
966
+ colors: list[ManimColor], length: int
967
+ ) -> ManimColor | list[ManimColor]:
968
+ """This method is currently not implemented. Refer to :func:`color_gradient` for
969
+ a working implementation for now.
970
+ """
658
971
  # TODO: implement proper gradient, research good implementation for this or look at 3b1b implementation
659
972
  raise NotImplementedError
660
973
 
@@ -669,37 +982,240 @@ class ManimColor:
669
982
  raise TypeError(
670
983
  f"Cannot compare {self.__class__.__name__} with {other.__class__.__name__}"
671
984
  )
672
- return np.allclose(self._internal_value, other._internal_value)
985
+ are_equal: bool = np.allclose(self._internal_value, other._internal_value)
986
+ return are_equal
987
+
988
+ def __add__(self, other: int | float | Self) -> Self:
989
+ if isinstance(other, (int, float)):
990
+ return self._construct_from_space(self._internal_space + other)
991
+ else:
992
+ return self._construct_from_space(
993
+ self._internal_space + other._internal_space
994
+ )
995
+
996
+ def __radd__(self, other: int | float | Self) -> Self:
997
+ return self + other
998
+
999
+ def __sub__(self, other: int | float | Self) -> Self:
1000
+ if isinstance(other, (int, float)):
1001
+ return self._construct_from_space(self._internal_space - other)
1002
+ else:
1003
+ return self._construct_from_space(
1004
+ self._internal_space - other._internal_space
1005
+ )
1006
+
1007
+ def __rsub__(self, other: int | float | Self) -> Self:
1008
+ return self - other
1009
+
1010
+ def __mul__(self, other: int | float | Self) -> Self:
1011
+ if isinstance(other, (int, float)):
1012
+ return self._construct_from_space(self._internal_space * other)
1013
+ else:
1014
+ return self._construct_from_space(
1015
+ self._internal_space * other._internal_space
1016
+ )
1017
+
1018
+ def __rmul__(self, other: int | float | Self) -> Self:
1019
+ return self * other
1020
+
1021
+ def __truediv__(self, other: int | float | Self) -> Self:
1022
+ if isinstance(other, (int, float)):
1023
+ return self._construct_from_space(self._internal_space / other)
1024
+ else:
1025
+ return self._construct_from_space(
1026
+ self._internal_space / other._internal_space
1027
+ )
1028
+
1029
+ def __rtruediv__(self, other: int | float | Self) -> Self:
1030
+ return self / other
1031
+
1032
+ def __floordiv__(self, other: int | float | Self) -> Self:
1033
+ if isinstance(other, (int, float)):
1034
+ return self._construct_from_space(self._internal_space // other)
1035
+ else:
1036
+ return self._construct_from_space(
1037
+ self._internal_space // other._internal_space
1038
+ )
1039
+
1040
+ def __rfloordiv__(self, other: int | float | Self) -> Self:
1041
+ return self // other
1042
+
1043
+ def __mod__(self, other: int | float | Self) -> Self:
1044
+ if isinstance(other, (int, float)):
1045
+ return self._construct_from_space(self._internal_space % other)
1046
+ else:
1047
+ return self._construct_from_space(
1048
+ self._internal_space % other._internal_space
1049
+ )
1050
+
1051
+ def __rmod__(self, other: int | float | Self) -> Self:
1052
+ return self % other
1053
+
1054
+ def __pow__(self, other: int | float | Self) -> Self:
1055
+ if isinstance(other, (int, float)):
1056
+ return self._construct_from_space(self._internal_space**other)
1057
+ else:
1058
+ return self._construct_from_space(
1059
+ self._internal_space**other._internal_space
1060
+ )
1061
+
1062
+ def __rpow__(self, other: int | float | Self) -> Self:
1063
+ return self**other
1064
+
1065
+ def __invert__(self) -> Self:
1066
+ return self.invert()
1067
+
1068
+ def __int__(self) -> int:
1069
+ return self.to_integer()
1070
+
1071
+ def __getitem__(self, index: int) -> float:
1072
+ item: float = self._internal_space[index]
1073
+ return item
1074
+
1075
+ def __and__(self, other: Self) -> Self:
1076
+ return self._construct_from_space(
1077
+ self._internal_from_integer(self.to_integer() & int(other), 1.0)
1078
+ )
1079
+
1080
+ def __or__(self, other: Self) -> Self:
1081
+ return self._construct_from_space(
1082
+ self._internal_from_integer(self.to_integer() | int(other), 1.0)
1083
+ )
1084
+
1085
+ def __xor__(self, other: Self) -> Self:
1086
+ return self._construct_from_space(
1087
+ self._internal_from_integer(self.to_integer() ^ int(other), 1.0)
1088
+ )
1089
+
1090
+ def __hash__(self) -> int:
1091
+ return hash(self.to_hex(with_alpha=True))
1092
+
1093
+
1094
+ RGBA = ManimColor
1095
+ """RGBA Color Space"""
1096
+
1097
+
1098
+ class HSV(ManimColor):
1099
+ """HSV Color Space"""
1100
+
1101
+ def __init__(
1102
+ self,
1103
+ hsv: HSV_Array_Float | HSV_Tuple_Float | HSVA_Array_Float | HSVA_Tuple_Float,
1104
+ alpha: float = 1.0,
1105
+ ) -> None:
1106
+ super().__init__(None)
1107
+ self.__hsv: HSVA_Array_Float
1108
+ if len(hsv) == 3:
1109
+ self.__hsv = np.asarray((*hsv, alpha))
1110
+ elif len(hsv) == 4:
1111
+ self.__hsv = np.asarray(hsv)
1112
+ else:
1113
+ raise ValueError("HSV Color must be an array of 3 values")
1114
+
1115
+ @classmethod
1116
+ @override
1117
+ def _from_internal(cls, value: ManimColorInternal) -> Self:
1118
+ hsv = colorsys.rgb_to_hsv(*value[:3])
1119
+ hsva = [*hsv, value[-1]]
1120
+ return cls(np.array(hsva))
1121
+
1122
+ @property
1123
+ def hue(self) -> float:
1124
+ hue: float = self.__hsv[0]
1125
+ return hue
1126
+
1127
+ @hue.setter
1128
+ def hue(self, hue: float) -> None:
1129
+ self.__hsv[0] = hue
1130
+
1131
+ @property
1132
+ def saturation(self) -> float:
1133
+ saturation: float = self.__hsv[1]
1134
+ return saturation
1135
+
1136
+ @saturation.setter
1137
+ def saturation(self, saturation: float) -> None:
1138
+ self.__hsv[1] = saturation
673
1139
 
674
- def __add__(self, other: ManimColor) -> ManimColor:
675
- return ManimColor(self._internal_value + other._internal_value)
1140
+ @property
1141
+ def value(self) -> float:
1142
+ value: float = self.__hsv[2]
1143
+ return value
676
1144
 
677
- def __sub__(self, other: ManimColor) -> ManimColor:
678
- return ManimColor(self._internal_value - other._internal_value)
1145
+ @value.setter
1146
+ def value(self, value: float) -> None:
1147
+ self.__hsv[2] = value
679
1148
 
680
- def __mul__(self, other: ManimColor) -> ManimColor:
681
- return ManimColor(self._internal_value * other._internal_value)
1149
+ @property
1150
+ def h(self) -> float:
1151
+ hue: float = self.__hsv[0]
1152
+ return hue
682
1153
 
683
- def __truediv__(self, other: ManimColor) -> ManimColor:
684
- return ManimColor(self._internal_value / other._internal_value)
1154
+ @h.setter
1155
+ def h(self, hue: float) -> None:
1156
+ self.__hsv[0] = hue
685
1157
 
686
- def __floordiv__(self, other: ManimColor) -> ManimColor:
687
- return ManimColor(self._internal_value // other._internal_value)
1158
+ @property
1159
+ def s(self) -> float:
1160
+ saturation: float = self.__hsv[1]
1161
+ return saturation
688
1162
 
689
- def __mod__(self, other: ManimColor) -> ManimColor:
690
- return ManimColor(self._internal_value % other._internal_value)
1163
+ @s.setter
1164
+ def s(self, saturation: float) -> None:
1165
+ self.__hsv[1] = saturation
691
1166
 
692
- def __pow__(self, other: ManimColor) -> ManimColor:
693
- return ManimColor(self._internal_value**other._internal_value)
1167
+ @property
1168
+ def v(self) -> float:
1169
+ value: float = self.__hsv[2]
1170
+ return value
694
1171
 
695
- def __and__(self, other: ManimColor) -> ManimColor:
696
- return ManimColor(self.to_integer() & other.to_integer())
1172
+ @v.setter
1173
+ def v(self, value: float) -> None:
1174
+ self.__hsv[2] = value
1175
+
1176
+ @property
1177
+ def _internal_space(self) -> npt.NDArray:
1178
+ return self.__hsv
1179
+
1180
+ @property
1181
+ def _internal_value(self) -> ManimColorInternal:
1182
+ """Return the internal value of the current :class:`ManimColor` as an
1183
+ ``[r,g,b,a]`` float array.
1184
+
1185
+ Returns
1186
+ -------
1187
+ ManimColorInternal
1188
+ Internal color representation.
1189
+ """
1190
+ return np.array(
1191
+ [
1192
+ *colorsys.hsv_to_rgb(self.__hsv[0], self.__hsv[1], self.__hsv[2]),
1193
+ self.__alpha,
1194
+ ],
1195
+ dtype=ManimColorDType,
1196
+ )
1197
+
1198
+ @_internal_value.setter
1199
+ def _internal_value(self, value: ManimColorInternal) -> None:
1200
+ """Overwrite the internal color value of this :class:`ManimColor`.
697
1201
 
698
- def __or__(self, other: ManimColor) -> ManimColor:
699
- return ManimColor(self.to_integer() | other.to_integer())
1202
+ Parameters
1203
+ ----------
1204
+ value
1205
+ The value which will overwrite the current color.
700
1206
 
701
- def __xor__(self, other: ManimColor) -> ManimColor:
702
- return ManimColor(self.to_integer() ^ other.to_integer())
1207
+ Raises
1208
+ ------
1209
+ TypeError
1210
+ If an invalid array is passed.
1211
+ """
1212
+ if not isinstance(value, np.ndarray):
1213
+ raise TypeError("Value must be a NumPy array.")
1214
+ if value.shape[0] != 4:
1215
+ raise TypeError("Array must have exactly 4 values.")
1216
+ tmp = colorsys.rgb_to_hsv(value[0], value[1], value[2])
1217
+ self.__hsv = np.array(tmp)
1218
+ self.__alpha = value[3]
703
1219
 
704
1220
 
705
1221
  ParsableManimColor: TypeAlias = Union[
@@ -715,76 +1231,84 @@ ParsableManimColor: TypeAlias = Union[
715
1231
  RGBA_Array_Int,
716
1232
  RGBA_Array_Float,
717
1233
  ]
718
- """ParsableManimColor is the representation for all types that are parsable to a color in manim"""
1234
+ """`ParsableManimColor` represents all the types which can be parsed
1235
+ to a :class:`ManimColor` in Manim.
1236
+ """
719
1237
 
720
1238
 
721
1239
  ManimColorT = TypeVar("ManimColorT", bound=ManimColor)
722
1240
 
723
1241
 
724
1242
  def color_to_rgb(color: ParsableManimColor) -> RGB_Array_Float:
725
- """Helper function for use in functional style programming refer to :meth:`to_rgb` in :class:`ManimColor`
1243
+ """Helper function for use in functional style programming.
1244
+ Refer to :meth:`ManimColor.to_rgb`.
726
1245
 
727
1246
  Parameters
728
1247
  ----------
729
- color : ParsableManimColor
730
- A color
1248
+ color
1249
+ A color to convert to an RGB float array.
731
1250
 
732
1251
  Returns
733
1252
  -------
734
1253
  RGB_Array_Float
735
- the corresponding rgb array
1254
+ The corresponding RGB float array.
736
1255
  """
737
1256
  return ManimColor(color).to_rgb()
738
1257
 
739
1258
 
740
- def color_to_rgba(color: ParsableManimColor, alpha: float = 1) -> RGBA_Array_Float:
741
- """Helper function for use in functional style programming refer to :meth:`to_rgba_with_alpha` in :class:`ManimColor`
1259
+ def color_to_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Array_Float:
1260
+ """Helper function for use in functional style programming. Refer to
1261
+ :meth:`ManimColor.to_rgba_with_alpha`.
742
1262
 
743
1263
  Parameters
744
1264
  ----------
745
- color : ParsableManimColor
746
- A color
747
- alpha : float, optional
748
- alpha value to be used in the color, by default 1
1265
+ color
1266
+ A color to convert to an RGBA float array.
1267
+ alpha
1268
+ An alpha value between 0.0 and 1.0 to be used as opacity in the color. Default is
1269
+ 1.0.
749
1270
 
750
1271
  Returns
751
1272
  -------
752
1273
  RGBA_Array_Float
753
- the corresponding rgba array
1274
+ The corresponding RGBA float array.
754
1275
  """
755
1276
  return ManimColor(color).to_rgba_with_alpha(alpha)
756
1277
 
757
1278
 
758
1279
  def color_to_int_rgb(color: ParsableManimColor) -> RGB_Array_Int:
759
- """Helper function for use in functional style programming refer to :meth:`to_int_rgb` in :class:`ManimColor`
1280
+ """Helper function for use in functional style programming. Refer to
1281
+ :meth:`ManimColor.to_int_rgb`.
760
1282
 
761
1283
  Parameters
762
1284
  ----------
763
- color : ParsableManimColor
764
- A color
1285
+ color
1286
+ A color to convert to an RGB integer array.
765
1287
 
766
1288
  Returns
767
1289
  -------
768
1290
  RGB_Array_Int
769
- the corresponding int rgb array
1291
+ The corresponding RGB integer array.
770
1292
  """
771
1293
  return ManimColor(color).to_int_rgb()
772
1294
 
773
1295
 
774
1296
  def color_to_int_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Array_Int:
775
- """Helper function for use in functional style programming refer to :meth:`to_int_rgba_with_alpha` in :class:`ManimColor`
1297
+ """Helper function for use in functional style programming. Refer to
1298
+ :meth:`ManimColor.to_int_rgba_with_alpha`.
776
1299
 
777
1300
  Parameters
778
1301
  ----------
779
- color : ParsableManimColor
780
- A color
781
- alpha : float, optional
782
- alpha value to be used in the color, by default 1.0
1302
+ color
1303
+ A color to convert to an RGBA integer array.
1304
+ alpha
1305
+ An alpha value between 0.0 and 1.0 to be used as opacity in the color. Default is
1306
+ 1.0.
783
1307
 
784
1308
  Returns
785
1309
  -------
786
1310
  RGBA_Array_Int
787
- the corresponding int rgba array
1311
+ The corresponding RGBA integer array.
788
1312
  """
789
1313
  return ManimColor(color).to_int_rgba_with_alpha(alpha)
790
1314
 
@@ -792,17 +1316,18 @@ def color_to_int_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Arr
792
1316
  def rgb_to_color(
793
1317
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
794
1318
  ) -> ManimColor:
795
- """Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor`
1319
+ """Helper function for use in functional style programming. Refer to
1320
+ :meth:`ManimColor.from_rgb`.
796
1321
 
797
1322
  Parameters
798
1323
  ----------
799
- rgb : RGB_Array_Float | RGB_Tuple_Float
800
- A 3 element iterable
1324
+ rgb
1325
+ A 3 element iterable.
801
1326
 
802
1327
  Returns
803
1328
  -------
804
1329
  ManimColor
805
- A ManimColor with the corresponding value
1330
+ A ManimColor with the corresponding value.
806
1331
  """
807
1332
  return ManimColor.from_rgb(rgb)
808
1333
 
@@ -810,12 +1335,13 @@ def rgb_to_color(
810
1335
  def rgba_to_color(
811
1336
  rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int,
812
1337
  ) -> ManimColor:
813
- """Helper function for use in functional style programming refer to :meth:`from_rgba` in :class:`ManimColor`
1338
+ """Helper function for use in functional style programming. Refer to
1339
+ :meth:`ManimColor.from_rgba`.
814
1340
 
815
1341
  Parameters
816
1342
  ----------
817
- rgba : RGBA_Array_Float | RGBA_Tuple_Float
818
- A 4 element iterable
1343
+ rgba
1344
+ A 4 element iterable.
819
1345
 
820
1346
  Returns
821
1347
  -------
@@ -828,92 +1354,74 @@ def rgba_to_color(
828
1354
  def rgb_to_hex(
829
1355
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
830
1356
  ) -> str:
831
- """Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor`
1357
+ """Helper function for use in functional style programming. Refer to
1358
+ :meth:`ManimColor.from_rgb` and :meth:`ManimColor.to_hex`.
832
1359
 
833
1360
  Parameters
834
1361
  ----------
835
- rgb : RGB_Array_Float | RGB_Tuple_Float
836
- A 3 element iterable
1362
+ rgb
1363
+ A 3 element iterable.
837
1364
 
838
1365
  Returns
839
1366
  -------
840
1367
  str
841
- A hex representation of the color, refer to :meth:`to_hex` in :class:`ManimColor`
1368
+ A hex representation of the color.
842
1369
  """
843
1370
  return ManimColor.from_rgb(rgb).to_hex()
844
1371
 
845
1372
 
846
1373
  def hex_to_rgb(hex_code: str) -> RGB_Array_Float:
847
- """Helper function for use in functional style programming refer to :meth:`to_hex` in :class:`ManimColor`
1374
+ """Helper function for use in functional style programming. Refer to
1375
+ :meth:`ManimColor.to_rgb`.
848
1376
 
849
1377
  Parameters
850
1378
  ----------
851
- hex_code : str
852
- A hex string representing a color
1379
+ hex_code
1380
+ A hex string representing a color.
853
1381
 
854
1382
  Returns
855
1383
  -------
856
1384
  RGB_Array_Float
857
- RGB array representing the color
1385
+ An RGB array representing the color.
858
1386
  """
859
1387
  return ManimColor(hex_code).to_rgb()
860
1388
 
861
1389
 
862
1390
  def invert_color(color: ManimColorT) -> ManimColorT:
863
- """Helper function for use in functional style programming refer to :meth:`invert` in :class:`ManimColor`
1391
+ """Helper function for use in functional style programming. Refer to
1392
+ :meth:`ManimColor.invert`
864
1393
 
865
1394
  Parameters
866
1395
  ----------
867
- color : ManimColor
868
- A ManimColor
1396
+ color
1397
+ The :class:`ManimColor` to invert.
869
1398
 
870
1399
  Returns
871
1400
  -------
872
1401
  ManimColor
873
- The linearly inverted ManimColor
1402
+ The linearly inverted :class:`ManimColor`.
874
1403
  """
875
1404
  return color.invert()
876
1405
 
877
1406
 
878
- def interpolate_arrays(
879
- arr1: npt.NDArray[Any], arr2: npt.NDArray[Any], alpha: float
880
- ) -> np.ndarray:
881
- """Helper function used in Manim to fade between two objects smoothly
882
-
883
- Parameters
884
- ----------
885
- arr1 : npt.NDArray[Any]
886
- The first array of colors
887
- arr2 : npt.NDArray[Any]
888
- The second array of colors
889
- alpha : float
890
- The alpha value corresponding to the interpolation point between the two inputs
891
-
892
- Returns
893
- -------
894
- np.ndarray
895
- The interpolated value of the to arrays
896
- """
897
- return (1 - alpha) * arr1 + alpha * arr2
898
-
899
-
900
1407
  def color_gradient(
901
1408
  reference_colors: Sequence[ParsableManimColor],
902
1409
  length_of_output: int,
903
1410
  ) -> list[ManimColor] | ManimColor:
904
- """Creates a list of colors interpolated between the input array of colors with a specific number of colors
1411
+ """Create a list of colors interpolated between the input array of colors with a
1412
+ specific number of colors.
905
1413
 
906
1414
  Parameters
907
1415
  ----------
908
- reference_colors : Sequence[ParsableManimColor]
909
- The colors to be interpolated between or spread apart
910
- length_of_output : int
911
- The number of colors that the output should have, ideally more than the input
1416
+ reference_colors
1417
+ The colors to be interpolated between or spread apart.
1418
+ length_of_output
1419
+ The number of colors that the output should have, ideally more than the input.
912
1420
 
913
1421
  Returns
914
1422
  -------
915
1423
  list[ManimColor] | ManimColor
916
- A list of ManimColor's which has the interpolated colors
1424
+ A :class:`ManimColor` or a list of interpolated :class:`ManimColor`'s.
917
1425
  """
918
1426
  if length_of_output == 0:
919
1427
  return ManimColor(reference_colors[0])
@@ -933,34 +1441,39 @@ def color_gradient(
933
1441
 
934
1442
 
935
1443
  def interpolate_color(
936
- color1: ManimColorT, color2: ManimColor, alpha: float
1444
+ color1: ManimColorT, color2: ManimColorT, alpha: float
937
1445
  ) -> ManimColorT:
938
- """Standalone function to interpolate two ManimColors and get the result refer to :meth:`interpolate` in :class:`ManimColor`
1446
+ """Standalone function to interpolate two ManimColors and get the result. Refer to
1447
+ :meth:`ManimColor.interpolate`.
939
1448
 
940
1449
  Parameters
941
1450
  ----------
942
- color1 : ManimColor
943
- First ManimColor
944
- color2 : ManimColor
945
- Second ManimColor
946
- alpha : float
947
- The alpha value determining the point of interpolation between the colors
1451
+ color1
1452
+ The first :class:`ManimColor`.
1453
+ color2
1454
+ The second :class:`ManimColor`.
1455
+ alpha
1456
+ The alpha value determining the point of interpolation between the colors.
948
1457
 
949
1458
  Returns
950
1459
  -------
951
1460
  ManimColor
952
- The interpolated ManimColor
1461
+ The interpolated ManimColor.
953
1462
  """
954
1463
  return color1.interpolate(color2, alpha)
955
1464
 
956
1465
 
957
1466
  def average_color(*colors: ParsableManimColor) -> ManimColor:
958
- """Determines the Average color of the given parameters
1467
+ """Determine the average color between the given parameters.
1468
+
1469
+ .. note::
1470
+ This operation does not consider the alphas (opacities) of the colors. The
1471
+ generated color has an alpha or opacity of 1.0.
959
1472
 
960
1473
  Returns
961
1474
  -------
962
1475
  ManimColor
963
- The average color of the input
1476
+ The average color of the input.
964
1477
  """
965
1478
  rgbs = np.array([color_to_rgb(color) for color in colors])
966
1479
  mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
@@ -968,31 +1481,31 @@ def average_color(*colors: ParsableManimColor) -> ManimColor:
968
1481
 
969
1482
 
970
1483
  def random_bright_color() -> ManimColor:
971
- """Returns you a random bright color
1484
+ """Return a random bright color: a random color averaged with ``WHITE``.
972
1485
 
973
1486
  .. warning::
974
- This operation is very expensive please keep in mind the performance loss.
1487
+ This operation is very expensive. Please keep in mind the performance loss.
975
1488
 
976
1489
  Returns
977
1490
  -------
978
1491
  ManimColor
979
- A bright ManimColor
1492
+ A random bright :class:`ManimColor`.
980
1493
  """
981
1494
  curr_rgb = color_to_rgb(random_color())
982
- new_rgb = interpolate_arrays(curr_rgb, np.ones(len(curr_rgb)), 0.5)
1495
+ new_rgb = 0.5 * (curr_rgb + np.ones(3))
983
1496
  return ManimColor(new_rgb)
984
1497
 
985
1498
 
986
1499
  def random_color() -> ManimColor:
987
- """Return you a random ManimColor
1500
+ """Return a random :class:`ManimColor`.
988
1501
 
989
1502
  .. warning::
990
- This operation is very expensive please keep in mind the performance loss.
1503
+ This operation is very expensive. Please keep in mind the performance loss.
991
1504
 
992
1505
  Returns
993
1506
  -------
994
1507
  ManimColor
995
- _description_
1508
+ A random :class:`ManimColor`.
996
1509
  """
997
1510
  import manim.utils.color.manim_colors as manim_colors
998
1511
 
@@ -1000,17 +1513,38 @@ def random_color() -> ManimColor:
1000
1513
 
1001
1514
 
1002
1515
  def get_shaded_rgb(
1003
- rgb: npt.NDArray[Any],
1004
- point: npt.NDArray[Any],
1005
- unit_normal_vect: npt.NDArray[Any],
1006
- light_source: npt.NDArray[Any],
1007
- ) -> RGBA_Array_Float:
1516
+ rgb: RGB_Array_Float,
1517
+ point: Point3D,
1518
+ unit_normal_vect: Vector3D,
1519
+ light_source: Point3D,
1520
+ ) -> RGB_Array_Float:
1521
+ """Add light or shadow to the ``rgb`` color of some surface which is located at a
1522
+ given ``point`` in space and facing in the direction of ``unit_normal_vect``,
1523
+ depending on whether the surface is facing a ``light_source`` or away from it.
1524
+
1525
+ Parameters
1526
+ ----------
1527
+ rgb
1528
+ An RGB array of floats.
1529
+ point
1530
+ The location of the colored surface.
1531
+ unit_normal_vect
1532
+ The direction in which the colored surface is facing.
1533
+ light_source
1534
+ The location of a light source which might illuminate the surface.
1535
+
1536
+ Returns
1537
+ -------
1538
+ RGB_Array_Float
1539
+ The color with added light or shadow, depending on the direction of the colored
1540
+ surface.
1541
+ """
1008
1542
  to_sun = normalize(light_source - point)
1009
- factor = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3
1010
- if factor < 0:
1011
- factor *= 0.5
1012
- result = rgb + factor
1013
- return result
1543
+ light = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3
1544
+ if light < 0:
1545
+ light *= 0.5
1546
+ shaded_rgb: RGB_Array_Float = rgb + light
1547
+ return shaded_rgb
1014
1548
 
1015
1549
 
1016
1550
  __all__ = [
@@ -1026,11 +1560,12 @@ __all__ = [
1026
1560
  "rgb_to_hex",
1027
1561
  "hex_to_rgb",
1028
1562
  "invert_color",
1029
- "interpolate_arrays",
1030
1563
  "color_gradient",
1031
1564
  "interpolate_color",
1032
1565
  "average_color",
1033
1566
  "random_bright_color",
1034
1567
  "random_color",
1035
1568
  "get_shaded_rgb",
1569
+ "HSV",
1570
+ "RGBA",
1036
1571
  ]