manim 0.18.1__py3-none-any.whl → 0.19.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of manim might be problematic. Click here for more details.

Files changed (129) hide show
  1. manim/__main__.py +45 -12
  2. manim/_config/__init__.py +2 -2
  3. manim/_config/cli_colors.py +8 -4
  4. manim/_config/default.cfg +0 -2
  5. manim/_config/logger_utils.py +5 -0
  6. manim/_config/utils.py +29 -38
  7. manim/animation/animation.py +148 -8
  8. manim/animation/composition.py +16 -13
  9. manim/animation/creation.py +184 -8
  10. manim/animation/fading.py +5 -8
  11. manim/animation/indication.py +93 -26
  12. manim/animation/movement.py +21 -3
  13. manim/animation/rotation.py +2 -1
  14. manim/animation/specialized.py +3 -5
  15. manim/animation/speedmodifier.py +3 -3
  16. manim/animation/transform.py +4 -5
  17. manim/animation/updaters/mobject_update_utils.py +17 -14
  18. manim/camera/camera.py +2 -2
  19. manim/cli/__init__.py +17 -0
  20. manim/cli/cfg/group.py +52 -36
  21. manim/cli/checkhealth/checks.py +92 -76
  22. manim/cli/checkhealth/commands.py +12 -5
  23. manim/cli/default_group.py +148 -24
  24. manim/cli/init/commands.py +28 -23
  25. manim/cli/plugins/commands.py +13 -3
  26. manim/cli/render/commands.py +47 -42
  27. manim/cli/render/global_options.py +43 -9
  28. manim/cli/render/render_options.py +84 -19
  29. manim/constants.py +11 -4
  30. manim/mobject/frame.py +0 -1
  31. manim/mobject/geometry/arc.py +109 -75
  32. manim/mobject/geometry/boolean_ops.py +20 -17
  33. manim/mobject/geometry/labeled.py +300 -77
  34. manim/mobject/geometry/line.py +120 -60
  35. manim/mobject/geometry/polygram.py +109 -25
  36. manim/mobject/geometry/shape_matchers.py +35 -15
  37. manim/mobject/geometry/tips.py +36 -27
  38. manim/mobject/graph.py +48 -40
  39. manim/mobject/graphing/coordinate_systems.py +110 -45
  40. manim/mobject/graphing/functions.py +16 -10
  41. manim/mobject/graphing/number_line.py +23 -9
  42. manim/mobject/graphing/probability.py +2 -10
  43. manim/mobject/graphing/scale.py +6 -5
  44. manim/mobject/matrix.py +17 -19
  45. manim/mobject/mobject.py +149 -103
  46. manim/mobject/opengl/opengl_geometry.py +4 -8
  47. manim/mobject/opengl/opengl_mobject.py +506 -343
  48. manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
  49. manim/mobject/opengl/opengl_surface.py +1 -2
  50. manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
  51. manim/mobject/svg/brace.py +61 -13
  52. manim/mobject/svg/svg_mobject.py +2 -1
  53. manim/mobject/table.py +11 -12
  54. manim/mobject/text/code_mobject.py +186 -550
  55. manim/mobject/text/numbers.py +7 -7
  56. manim/mobject/text/tex_mobject.py +22 -13
  57. manim/mobject/text/text_mobject.py +29 -20
  58. manim/mobject/three_d/polyhedra.py +98 -1
  59. manim/mobject/three_d/three_dimensions.py +59 -31
  60. manim/mobject/types/image_mobject.py +37 -23
  61. manim/mobject/types/point_cloud_mobject.py +103 -67
  62. manim/mobject/types/vectorized_mobject.py +387 -214
  63. manim/mobject/value_tracker.py +2 -1
  64. manim/mobject/vector_field.py +2 -4
  65. manim/opengl/__init__.py +3 -3
  66. manim/plugins/__init__.py +2 -3
  67. manim/plugins/plugins_flags.py +3 -3
  68. manim/renderer/cairo_renderer.py +11 -11
  69. manim/renderer/opengl_renderer.py +19 -20
  70. manim/renderer/shader.py +2 -3
  71. manim/renderer/shader_wrapper.py +3 -2
  72. manim/scene/moving_camera_scene.py +23 -0
  73. manim/scene/scene.py +72 -41
  74. manim/scene/scene_file_writer.py +313 -164
  75. manim/scene/section.py +15 -15
  76. manim/scene/three_d_scene.py +8 -15
  77. manim/scene/vector_space_scene.py +3 -6
  78. manim/typing.py +326 -66
  79. manim/utils/bezier.py +1658 -381
  80. manim/utils/caching.py +11 -5
  81. manim/utils/color/AS2700.py +2 -0
  82. manim/utils/color/BS381.py +2 -0
  83. manim/utils/color/DVIPSNAMES.py +96 -0
  84. manim/utils/color/SVGNAMES.py +179 -0
  85. manim/utils/color/X11.py +3 -0
  86. manim/utils/color/XKCD.py +2 -0
  87. manim/utils/color/__init__.py +8 -5
  88. manim/utils/color/core.py +818 -301
  89. manim/utils/color/manim_colors.py +7 -9
  90. manim/utils/commands.py +40 -19
  91. manim/utils/config_ops.py +18 -13
  92. manim/utils/debug.py +8 -6
  93. manim/utils/deprecation.py +92 -43
  94. manim/utils/docbuild/autoaliasattr_directive.py +45 -8
  95. manim/utils/docbuild/autocolor_directive.py +12 -13
  96. manim/utils/docbuild/manim_directive.py +35 -29
  97. manim/utils/docbuild/module_parsing.py +74 -27
  98. manim/utils/family.py +3 -3
  99. manim/utils/family_ops.py +12 -4
  100. manim/utils/file_ops.py +22 -16
  101. manim/utils/hashing.py +7 -7
  102. manim/utils/images.py +10 -4
  103. manim/utils/ipython_magic.py +12 -8
  104. manim/utils/iterables.py +161 -119
  105. manim/utils/module_ops.py +55 -19
  106. manim/utils/opengl.py +68 -23
  107. manim/utils/parameter_parsing.py +3 -2
  108. manim/utils/paths.py +11 -5
  109. manim/utils/polylabel.py +168 -0
  110. manim/utils/qhull.py +218 -0
  111. manim/utils/rate_functions.py +69 -32
  112. manim/utils/simple_functions.py +24 -15
  113. manim/utils/sounds.py +7 -1
  114. manim/utils/space_ops.py +48 -37
  115. manim/utils/testing/_frames_testers.py +13 -8
  116. manim/utils/testing/_show_diff.py +5 -3
  117. manim/utils/testing/_test_class_makers.py +33 -18
  118. manim/utils/testing/frames_comparison.py +20 -14
  119. manim/utils/tex.py +4 -2
  120. manim/utils/tex_file_writing.py +45 -45
  121. manim/utils/tex_templates.py +1 -1
  122. manim/utils/unit.py +6 -5
  123. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
  124. manim-0.19.0.dist-info/RECORD +221 -0
  125. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
  126. manim-0.18.1.dist-info/RECORD +0 -217
  127. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
  128. {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
  129. {manim-0.18.1.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,43 +94,48 @@ 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
- 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)
56
105
 
57
106
 
58
107
  class ManimColor:
59
108
  """Internal representation of a color.
60
109
 
61
- The ManimColor class is the main class for the representation of a color.
62
- It's internal representation is a 4 element array of floats corresponding
63
- 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.
64
113
 
65
114
  This is done in order to reduce the amount of color inconsistencies by constantly
66
115
  casting between integers and floats which introduces errors.
67
116
 
68
117
  The class can accept any value of type :class:`ParsableManimColor` i.e.
69
118
 
70
- ManimColor, int, str, RGB_Tuple_Int, RGB_Tuple_Float, RGBA_Tuple_Int, RGBA_Tuple_Float, RGB_Array_Int,
71
- 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``
72
121
 
73
- ManimColor itself only accepts singular values and will directly interpret them into a single color if possible
74
- 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.
75
125
 
76
- If you want to parse a list of colors use the function :meth:`parse` in :class:`ManimColor` which assumes that
77
- you are going to pass a list of color so arrays will not be 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.
78
129
 
79
130
  .. warning::
80
- If you pass an array of numbers to :meth:`parse` it will interpret the r,g,b,a numbers in that array as colors
81
- 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.
82
134
 
83
- For conversion behaviors see the ``_internal`` functions for further documentation
135
+ For conversion behaviors, see the ``_internal`` functions for further documentation.
84
136
 
85
- You can create a ``ManimColor`` instance via its classmethods. See the respective methods for more info.
137
+ You can create a :class:`ManimColor` instance via its classmethods. See the
138
+ respective methods for more info.
86
139
 
87
140
  .. code-block:: python
88
141
 
@@ -131,14 +184,14 @@ class ManimColor:
131
184
  # This is not expected to be called on module initialization time
132
185
  # It can be horribly slow to convert a string to a color because
133
186
  # it has to access the dictionary of colors and find the right color
134
- self._internal_value = ManimColor._internal_from_string(value)
187
+ self._internal_value = ManimColor._internal_from_string(value, alpha)
135
188
  elif isinstance(value, (list, tuple, np.ndarray)):
136
189
  length = len(value)
137
190
  if all(isinstance(x, float) for x in value):
138
191
  if length == 3:
139
- 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]
140
193
  elif length == 4:
141
- self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore
194
+ self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore[arg-type]
142
195
  else:
143
196
  raise ValueError(
144
197
  f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
@@ -146,10 +199,11 @@ class ManimColor:
146
199
  else:
147
200
  if length == 3:
148
201
  self._internal_value = ManimColor._internal_from_int_rgb(
149
- value, alpha # type: ignore
202
+ value, # type: ignore[arg-type]
203
+ alpha,
150
204
  )
151
205
  elif length == 4:
152
- 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]
153
207
  else:
154
208
  raise ValueError(
155
209
  f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
@@ -158,7 +212,6 @@ class ManimColor:
158
212
  result = re_hex.search(value.get_hex())
159
213
  if result is None:
160
214
  raise ValueError(f"Failed to parse a color from {value}")
161
-
162
215
  self._internal_value = ManimColor._internal_from_hex_string(
163
216
  result.group(), alpha
164
217
  )
@@ -170,37 +223,59 @@ class ManimColor:
170
223
  f"list[float, float, float, float], not {type(value)}"
171
224
  )
172
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
+
173
234
  @property
174
235
  def _internal_value(self) -> ManimColorInternal:
175
- """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.
176
238
 
177
239
  Returns
178
240
  -------
179
241
  ManimColorInternal
180
- internal color representation
242
+ Internal color representation.
181
243
  """
182
244
  return self.__value
183
245
 
184
246
  @_internal_value.setter
185
247
  def _internal_value(self, value: ManimColorInternal) -> None:
186
- """Overwrites the internal color value of the ManimColor object
248
+ """Overwrite the internal color value of this :class:`ManimColor`.
187
249
 
188
250
  Parameters
189
251
  ----------
190
- value : ManimColorInternal
191
- The value which will overwrite the current color
252
+ value
253
+ The value which will overwrite the current color.
192
254
 
193
255
  Raises
194
256
  ------
195
257
  TypeError
196
- Raises a TypeError if an invalid array is passed
258
+ If an invalid array is passed.
197
259
  """
198
260
  if not isinstance(value, np.ndarray):
199
- raise TypeError("value must be a numpy array")
261
+ raise TypeError("Value must be a NumPy array.")
200
262
  if value.shape[0] != 4:
201
- raise TypeError("Array must have 4 values exactly")
263
+ raise TypeError("Array must have exactly 4 values.")
202
264
  self.__value: ManimColorInternal = value
203
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
+
204
279
  @staticmethod
205
280
  def _internal_from_integer(value: int, alpha: float) -> ManimColorInternal:
206
281
  return np.asarray(
@@ -213,32 +288,41 @@ class ManimColor:
213
288
  dtype=ManimColorDType,
214
289
  )
215
290
 
216
- # TODO: Maybe make 8 nibble hex also convertible ?
217
291
  @staticmethod
218
- def _internal_from_hex_string(hex: str, alpha: float) -> ManimColorInternal:
219
- """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`.
220
295
 
221
296
  .. warning::
222
297
  This does not accept any prefixes like # or similar in front of the hex string.
223
- This is just intended for the raw hex part
298
+ This is just intended for the raw hex part.
224
299
 
225
300
  *For internal use only*
226
301
 
227
302
  Parameters
228
303
  ----------
229
- hex : str
230
- hex string to be parsed
231
- alpha : float
232
- 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.
233
309
 
234
310
  Returns
235
311
  -------
236
312
  ManimColorInternal
237
313
  Internal color representation
238
314
  """
239
- if len(hex) == 6:
240
- hex += "00"
241
- 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)
242
326
  return np.asarray(
243
327
  (
244
328
  ((tmp >> 24) & 0xFF) / 255,
@@ -253,22 +337,22 @@ class ManimColor:
253
337
  def _internal_from_int_rgb(
254
338
  rgb: RGB_Tuple_Int, alpha: float = 1.0
255
339
  ) -> ManimColorInternal:
256
- """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`.
257
342
 
258
343
  *For internal use only*
259
344
 
260
345
  Parameters
261
346
  ----------
262
- rgb : RGB_Tuple_Int
263
- integer rgb tuple to be parsed
264
- alpha : float, optional
265
- 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.
266
351
 
267
352
  Returns
268
353
  -------
269
354
  ManimColorInternal
270
- Internal color representation
271
-
355
+ Internal color representation.
272
356
  """
273
357
  value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy() / 255
274
358
  value.resize(4, refcheck=False)
@@ -279,22 +363,22 @@ class ManimColor:
279
363
  def _internal_from_rgb(
280
364
  rgb: RGB_Tuple_Float, alpha: float = 1.0
281
365
  ) -> ManimColorInternal:
282
- """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`.
283
368
 
284
369
  *For internal use only*
285
370
 
286
371
  Parameters
287
372
  ----------
288
- rgb : RGB_Tuple_Float
289
- float rgb tuple to be parsed
290
-
291
- alpha : float, optional
292
- 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.
293
377
 
294
378
  Returns
295
379
  -------
296
380
  ManimColorInternal
297
- Internal color representation
381
+ Internal color representation.
298
382
  """
299
383
  value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy()
300
384
  value.resize(4, refcheck=False)
@@ -303,175 +387,178 @@ class ManimColor:
303
387
 
304
388
  @staticmethod
305
389
  def _internal_from_int_rgba(rgba: RGBA_Tuple_Int) -> ManimColorInternal:
306
- """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`.
307
392
 
308
393
  *For internal use only*
309
394
 
310
395
  Parameters
311
396
  ----------
312
- rgba : RGBA_Tuple_Int
313
- int rgba tuple to be parsed
397
+ rgba
398
+ Int RGBA tuple to be parsed.
314
399
 
315
400
  Returns
316
401
  -------
317
402
  ManimColorInternal
318
- Internal color representation
403
+ Internal color representation.
319
404
  """
320
405
  return np.asarray(rgba, dtype=ManimColorDType) / 255
321
406
 
322
407
  @staticmethod
323
408
  def _internal_from_rgba(rgba: RGBA_Tuple_Float) -> ManimColorInternal:
324
- """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`.
325
411
 
326
412
  *For internal use only*
327
413
 
328
414
  Parameters
329
415
  ----------
330
- rgba : RGBA_Tuple_Float
331
- int rgba tuple to be parsed
416
+ rgba
417
+ Int RGBA tuple to be parsed.
332
418
 
333
419
  Returns
334
420
  -------
335
421
  ManimColorInternal
336
- Internal color representation
422
+ Internal color representation.
337
423
  """
338
424
  return np.asarray(rgba, dtype=ManimColorDType)
339
425
 
340
426
  @staticmethod
341
- def _internal_from_string(name: str) -> ManimColorInternal:
342
- """Internal function for converting a string into the internal representation of a ManimColor.
343
- 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.
344
431
 
345
432
  *For internal use only*
346
433
 
347
434
  Parameters
348
435
  ----------
349
- name : str
350
- The color name to be parsed into a color. Refer to the different color Modules in the documentation Page to
351
- 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.
352
439
 
353
440
  Returns
354
441
  -------
355
442
  ManimColorInternal
356
- Internal color representation
443
+ Internal color representation.
357
444
 
358
445
  Raises
359
446
  ------
360
447
  ValueError
361
- Raises a ValueError if the color name is not present with manim
448
+ If the color name is not present in Manim.
362
449
  """
363
450
  from . import _all_color_dict
364
451
 
365
- upper_name = name.upper()
366
-
367
- if upper_name in _all_color_dict:
368
- 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()
369
455
  else:
370
456
  raise ValueError(f"Color {name} not found")
371
457
 
372
458
  def to_integer(self) -> int:
373
- """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.
374
463
 
375
464
  Returns
376
465
  -------
377
466
  int
378
- integer representation of the color
379
-
380
- .. warning::
381
- This will return only the rgb part of the color
467
+ Integer representation of the color.
382
468
  """
383
- return int.from_bytes(
384
- (self._internal_value[:3] * 255).astype(int).tobytes(), "big"
385
- )
469
+ tmp = (self._internal_value[:3] * 255).astype(dtype=np.byte).tobytes()
470
+ return int.from_bytes(tmp, "big")
386
471
 
387
472
  def to_rgb(self) -> RGB_Array_Float:
388
- """Converts the current ManimColor into a rgb array of floats
473
+ """Convert the current :class:`ManimColor` into an RGB array of floats.
389
474
 
390
475
  Returns
391
476
  -------
392
477
  RGB_Array_Float
393
- rgb array with 3 elements of type float
478
+ RGB array of 3 floats from 0.0 to 1.0.
394
479
  """
395
480
  return self._internal_value[:3]
396
481
 
397
482
  def to_int_rgb(self) -> RGB_Array_Int:
398
- """Converts the current ManimColor into a rgb array of int
483
+ """Convert the current :class:`ManimColor` into an RGB array of integers.
399
484
 
400
485
  Returns
401
486
  -------
402
487
  RGB_Array_Int
403
- rgb array with 3 elements of type int
488
+ RGB array of 3 integers from 0 to 255.
404
489
  """
405
490
  return (self._internal_value[:3] * 255).astype(int)
406
491
 
407
492
  def to_rgba(self) -> RGBA_Array_Float:
408
- """Converts the current ManimColor into a rgba array of floats
493
+ """Convert the current :class:`ManimColor` into an RGBA array of floats.
409
494
 
410
495
  Returns
411
496
  -------
412
497
  RGBA_Array_Float
413
- rgba array with 4 elements of type float
498
+ RGBA array of 4 floats from 0.0 to 1.0.
414
499
  """
415
500
  return self._internal_value
416
501
 
417
502
  def to_int_rgba(self) -> RGBA_Array_Int:
418
- """Converts the current ManimColor into a rgba array of int
503
+ """Convert the current ManimColor into an RGBA array of integers.
419
504
 
420
505
 
421
506
  Returns
422
507
  -------
423
508
  RGBA_Array_Int
424
- rgba array with 4 elements of type int
509
+ RGBA array of 4 integers from 0 to 255.
425
510
  """
426
511
  return (self._internal_value * 255).astype(int)
427
512
 
428
513
  def to_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Float:
429
- """Converts the current ManimColor into a rgba array of float as :meth:`to_rgba` but you can change the alpha
430
- 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.
431
516
 
432
517
  Parameters
433
518
  ----------
434
- alpha : float
435
- alpha value to be used in the return value
519
+ alpha
520
+ Alpha value to be used in the return value.
436
521
 
437
522
  Returns
438
523
  -------
439
524
  RGBA_Array_Float
440
- rgba array with 4 elements of type float
525
+ RGBA array of 4 floats from 0.0 to 1.0.
441
526
  """
442
527
  return np.fromiter((*self._internal_value[:3], alpha), dtype=ManimColorDType)
443
528
 
444
529
  def to_int_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Int:
445
- """Converts the current ManimColor into a rgba array of integers as :meth:`to_int_rgba` but you can change the alpha
446
- 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.
447
532
 
448
533
  Parameters
449
534
  ----------
450
- alpha : float
451
- 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.
452
538
 
453
539
  Returns
454
540
  -------
455
541
  RGBA_Array_Int
456
- rgba array with 4 elements of type int
542
+ RGBA array of 4 integers from 0 to 255.
457
543
  """
458
544
  tmp = self._internal_value * 255
459
545
  tmp[3] = alpha * 255
460
546
  return tmp.astype(int)
461
547
 
462
548
  def to_hex(self, with_alpha: bool = False) -> str:
463
- """Converts the manim color to a hexadecimal representation of the color
549
+ """Convert the :class:`ManimColor` to a hexadecimal representation of the color.
464
550
 
465
551
  Parameters
466
552
  ----------
467
- with_alpha : bool, optional
468
- Changes the result from 6 to 8 values where the last 2 nibbles represent the alpha value of 0-255,
469
- 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``.
470
556
 
471
557
  Returns
472
558
  -------
473
559
  str
474
- 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``.
475
562
  """
476
563
  tmp = (
477
564
  f"#{int(self._internal_value[0] * 255):02X}"
@@ -483,148 +570,344 @@ class ManimColor:
483
570
  return tmp
484
571
 
485
572
  def to_hsv(self) -> HSV_Array_Float:
486
- """Converts the Manim Color to HSV array.
573
+ """Convert the :class:`ManimColor` to an HSV array.
487
574
 
488
575
  .. note::
489
- Be careful this returns an array in the form `[h, s, v]` where the elements are floats.
490
- This might be confusing because rgb can also be an array of floats so you might want to annotate the usage
491
- of this function in your code by typing the variables with :class:`HSV_Array_Float` in order to differentiate
492
- 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.
493
581
 
494
582
  Returns
495
583
  -------
496
584
  HSV_Array_Float
497
- 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.
586
+ """
587
+ return np.array(colorsys.rgb_to_hsv(*self.to_rgb()))
588
+
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.
498
603
  """
499
- return colorsys.rgb_to_hsv(*self.to_rgb())
604
+ return np.array(colorsys.rgb_to_hls(*self.to_rgb()))
500
605
 
501
- def invert(self, with_alpha=False) -> ManimColor:
502
- """Returns an linearly inverted version of the color (no inplace changes)
606
+ def invert(self, with_alpha: bool = False) -> Self:
607
+ """Return a new, linearly inverted version of this :class:`ManimColor` (no
608
+ inplace changes).
503
609
 
504
610
  Parameters
505
611
  ----------
506
- with_alpha : bool, optional
507
- 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``.
508
614
 
509
615
  .. note::
510
- This can result in unintended behavior where objects are not displayed because their alpha
511
- 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.
512
619
 
513
620
  Returns
514
621
  -------
515
622
  ManimColor
516
- The linearly inverted ManimColor
623
+ The linearly inverted :class:`ManimColor`.
517
624
  """
518
- 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)
519
632
 
520
- def interpolate(self, other: ManimColor, alpha: float) -> ManimColor:
521
- """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.
522
636
 
523
637
  Parameters
524
638
  ----------
525
- other : ManimColor
526
- The other ManimColor to be used for interpolation
527
- alpha : float
528
- A point on the line in rgba colorspace connecting the two colors i.e. the interpolation point
529
-
530
- 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`.
531
645
 
532
646
  Returns
533
647
  -------
534
648
  ManimColor
535
- The interpolated ManimColor
649
+ The interpolated :class:`ManimColor`.
536
650
  """
537
- return ManimColor(
538
- 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
539
653
  )
540
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
+
541
795
  @classmethod
542
796
  def from_rgb(
543
797
  cls,
544
798
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
545
799
  alpha: float = 1.0,
546
800
  ) -> Self:
547
- """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``.
548
803
 
549
804
  .. warning::
550
- Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
551
- 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``.
552
808
 
553
809
 
554
810
  Parameters
555
811
  ----------
556
- rgb : RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int
557
- Any 3 Element Iterable
558
- alpha : float, optional
559
- 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.
560
816
 
561
817
  Returns
562
818
  -------
563
819
  ManimColor
564
- Returns the ManimColor object
820
+ The :class:`ManimColor` which corresponds to the given ``rgb``.
565
821
  """
566
- return cls(rgb, alpha)
822
+ return cls._from_internal(ManimColor(rgb, alpha)._internal_value)
567
823
 
568
824
  @classmethod
569
825
  def from_rgba(
570
826
  cls, rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
571
827
  ) -> Self:
572
- """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``.
573
830
 
574
831
  .. warning::
575
- Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
576
- 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``.
577
835
 
578
836
  Parameters
579
837
  ----------
580
- rgba : RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
581
- Any 4 Element Iterable
838
+ rgba
839
+ Any iterable of 4 floats or 4 integers.
582
840
 
583
841
  Returns
584
842
  -------
585
843
  ManimColor
586
- Returns the ManimColor object
844
+ The :class:`ManimColor` corresponding to the given ``rgba``.
587
845
  """
588
846
  return cls(rgba)
589
847
 
590
848
  @classmethod
591
- def from_hex(cls, hex: str, alpha: float = 1.0) -> Self:
592
- """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.
593
851
 
594
852
  Parameters
595
853
  ----------
596
- hex : str
597
- The hex string to be converted (currently only supports 6 nibbles)
598
- alpha : float, optional
599
- 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.
600
860
 
601
861
  Returns
602
862
  -------
603
863
  ManimColor
604
- The ManimColor represented by the hex string
864
+ The :class:`ManimColor` represented by the hex string.
605
865
  """
606
- return cls(hex, alpha)
866
+ return cls._from_internal(ManimColor(hex_str, alpha)._internal_value)
607
867
 
608
868
  @classmethod
609
869
  def from_hsv(
610
870
  cls, hsv: HSV_Array_Float | HSV_Tuple_Float, alpha: float = 1.0
611
871
  ) -> Self:
612
- """Creates a ManimColor from an HSV Array
872
+ """Create a :class:`ManimColor` from an HSV array.
613
873
 
614
874
  Parameters
615
875
  ----------
616
- hsv : HSV_Array_Float | HSV_Tuple_Float
617
- Any 3 Element Iterable containing floats from 0-1
618
- alpha : float, optional
619
- 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.
620
880
 
621
881
  Returns
622
882
  -------
623
883
  ManimColor
624
- 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.
625
886
  """
626
887
  rgb = colorsys.hsv_to_rgb(*hsv)
627
- 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)
628
911
 
629
912
  @overload
630
913
  @classmethod
@@ -645,31 +928,46 @@ class ManimColor:
645
928
  @classmethod
646
929
  def parse(
647
930
  cls,
648
- color: ParsableManimColor | list[ParsableManimColor] | None,
931
+ color: ParsableManimColor | Sequence[ParsableManimColor] | None,
649
932
  alpha: float = 1.0,
650
933
  ) -> Self | list[Self]:
651
- """
652
- 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.
653
936
 
654
937
  Parameters
655
938
  ----------
656
939
  color
657
- 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]``.
658
943
  alpha
659
- 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).
660
945
 
661
946
  Returns
662
947
  -------
663
- ManimColor
664
- 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.
665
950
  """
666
- if isinstance(color, (list, tuple)):
667
- return [cls(c, alpha) for c in color] # type: ignore
668
- 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)
669
963
 
670
964
  @staticmethod
671
- def gradient(colors: list[ManimColor], length: int):
672
- """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
+ """
673
971
  # TODO: implement proper gradient, research good implementation for this or look at 3b1b implementation
674
972
  raise NotImplementedError
675
973
 
@@ -684,37 +982,240 @@ class ManimColor:
684
982
  raise TypeError(
685
983
  f"Cannot compare {self.__class__.__name__} with {other.__class__.__name__}"
686
984
  )
687
- 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
+ )
688
1079
 
689
- def __add__(self, other: ManimColor) -> ManimColor:
690
- return ManimColor(self._internal_value + other._internal_value)
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
+ )
691
1084
 
692
- def __sub__(self, other: ManimColor) -> ManimColor:
693
- return ManimColor(self._internal_value - other._internal_value)
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
+ )
694
1089
 
695
- def __mul__(self, other: ManimColor) -> ManimColor:
696
- return ManimColor(self._internal_value * other._internal_value)
1090
+ def __hash__(self) -> int:
1091
+ return hash(self.to_hex(with_alpha=True))
697
1092
 
698
- def __truediv__(self, other: ManimColor) -> ManimColor:
699
- return ManimColor(self._internal_value / other._internal_value)
700
1093
 
701
- def __floordiv__(self, other: ManimColor) -> ManimColor:
702
- return ManimColor(self._internal_value // other._internal_value)
1094
+ RGBA = ManimColor
1095
+ """RGBA Color Space"""
703
1096
 
704
- def __mod__(self, other: ManimColor) -> ManimColor:
705
- return ManimColor(self._internal_value % other._internal_value)
706
1097
 
707
- def __pow__(self, other: ManimColor) -> ManimColor:
708
- return ManimColor(self._internal_value**other._internal_value)
1098
+ class HSV(ManimColor):
1099
+ """HSV Color Space"""
709
1100
 
710
- def __and__(self, other: ManimColor) -> ManimColor:
711
- return ManimColor(self.to_integer() & other.to_integer())
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")
712
1114
 
713
- def __or__(self, other: ManimColor) -> ManimColor:
714
- return ManimColor(self.to_integer() | other.to_integer())
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))
715
1121
 
716
- def __xor__(self, other: ManimColor) -> ManimColor:
717
- return ManimColor(self.to_integer() ^ other.to_integer())
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
1139
+
1140
+ @property
1141
+ def value(self) -> float:
1142
+ value: float = self.__hsv[2]
1143
+ return value
1144
+
1145
+ @value.setter
1146
+ def value(self, value: float) -> None:
1147
+ self.__hsv[2] = value
1148
+
1149
+ @property
1150
+ def h(self) -> float:
1151
+ hue: float = self.__hsv[0]
1152
+ return hue
1153
+
1154
+ @h.setter
1155
+ def h(self, hue: float) -> None:
1156
+ self.__hsv[0] = hue
1157
+
1158
+ @property
1159
+ def s(self) -> float:
1160
+ saturation: float = self.__hsv[1]
1161
+ return saturation
1162
+
1163
+ @s.setter
1164
+ def s(self, saturation: float) -> None:
1165
+ self.__hsv[1] = saturation
1166
+
1167
+ @property
1168
+ def v(self) -> float:
1169
+ value: float = self.__hsv[2]
1170
+ return value
1171
+
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`.
1201
+
1202
+ Parameters
1203
+ ----------
1204
+ value
1205
+ The value which will overwrite the current color.
1206
+
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]
718
1219
 
719
1220
 
720
1221
  ParsableManimColor: TypeAlias = Union[
@@ -731,7 +1232,7 @@ ParsableManimColor: TypeAlias = Union[
731
1232
  RGBA_Array_Float,
732
1233
  ]
733
1234
  """`ParsableManimColor` represents all the types which can be parsed
734
- to a color in Manim.
1235
+ to a :class:`ManimColor` in Manim.
735
1236
  """
736
1237
 
737
1238
 
@@ -740,69 +1241,74 @@ ManimColorT = TypeVar("ManimColorT", bound=ManimColor)
740
1241
 
741
1242
  def color_to_rgb(color: ParsableManimColor) -> RGB_Array_Float:
742
1243
  """Helper function for use in functional style programming.
743
- Refer to :meth:`to_rgb` in :class:`ManimColor`.
1244
+ Refer to :meth:`ManimColor.to_rgb`.
744
1245
 
745
1246
  Parameters
746
1247
  ----------
747
- color : ParsableManimColor
748
- A color
1248
+ color
1249
+ A color to convert to an RGB float array.
749
1250
 
750
1251
  Returns
751
1252
  -------
752
1253
  RGB_Array_Float
753
- the corresponding rgb array
1254
+ The corresponding RGB float array.
754
1255
  """
755
1256
  return ManimColor(color).to_rgb()
756
1257
 
757
1258
 
758
- def color_to_rgba(color: ParsableManimColor, alpha: float = 1) -> RGBA_Array_Float:
759
- """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`.
760
1262
 
761
1263
  Parameters
762
1264
  ----------
763
- color : ParsableManimColor
764
- A color
765
- alpha : float, optional
766
- 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.
767
1270
 
768
1271
  Returns
769
1272
  -------
770
1273
  RGBA_Array_Float
771
- the corresponding rgba array
1274
+ The corresponding RGBA float array.
772
1275
  """
773
1276
  return ManimColor(color).to_rgba_with_alpha(alpha)
774
1277
 
775
1278
 
776
1279
  def color_to_int_rgb(color: ParsableManimColor) -> RGB_Array_Int:
777
- """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`.
778
1282
 
779
1283
  Parameters
780
1284
  ----------
781
- color : ParsableManimColor
782
- A color
1285
+ color
1286
+ A color to convert to an RGB integer array.
783
1287
 
784
1288
  Returns
785
1289
  -------
786
1290
  RGB_Array_Int
787
- the corresponding int rgb array
1291
+ The corresponding RGB integer array.
788
1292
  """
789
1293
  return ManimColor(color).to_int_rgb()
790
1294
 
791
1295
 
792
1296
  def color_to_int_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Array_Int:
793
- """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`.
794
1299
 
795
1300
  Parameters
796
1301
  ----------
797
- color : ParsableManimColor
798
- A color
799
- alpha : float, optional
800
- 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.
801
1307
 
802
1308
  Returns
803
1309
  -------
804
1310
  RGBA_Array_Int
805
- the corresponding int rgba array
1311
+ The corresponding RGBA integer array.
806
1312
  """
807
1313
  return ManimColor(color).to_int_rgba_with_alpha(alpha)
808
1314
 
@@ -810,17 +1316,18 @@ def color_to_int_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Arr
810
1316
  def rgb_to_color(
811
1317
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
812
1318
  ) -> ManimColor:
813
- """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`.
814
1321
 
815
1322
  Parameters
816
1323
  ----------
817
- rgb : RGB_Array_Float | RGB_Tuple_Float
818
- A 3 element iterable
1324
+ rgb
1325
+ A 3 element iterable.
819
1326
 
820
1327
  Returns
821
1328
  -------
822
1329
  ManimColor
823
- A ManimColor with the corresponding value
1330
+ A ManimColor with the corresponding value.
824
1331
  """
825
1332
  return ManimColor.from_rgb(rgb)
826
1333
 
@@ -828,12 +1335,13 @@ def rgb_to_color(
828
1335
  def rgba_to_color(
829
1336
  rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int,
830
1337
  ) -> ManimColor:
831
- """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`.
832
1340
 
833
1341
  Parameters
834
1342
  ----------
835
- rgba : RGBA_Array_Float | RGBA_Tuple_Float
836
- A 4 element iterable
1343
+ rgba
1344
+ A 4 element iterable.
837
1345
 
838
1346
  Returns
839
1347
  -------
@@ -846,92 +1354,74 @@ def rgba_to_color(
846
1354
  def rgb_to_hex(
847
1355
  rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
848
1356
  ) -> str:
849
- """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`.
850
1359
 
851
1360
  Parameters
852
1361
  ----------
853
- rgb : RGB_Array_Float | RGB_Tuple_Float
854
- A 3 element iterable
1362
+ rgb
1363
+ A 3 element iterable.
855
1364
 
856
1365
  Returns
857
1366
  -------
858
1367
  str
859
- A hex representation of the color, refer to :meth:`to_hex` in :class:`ManimColor`
1368
+ A hex representation of the color.
860
1369
  """
861
1370
  return ManimColor.from_rgb(rgb).to_hex()
862
1371
 
863
1372
 
864
1373
  def hex_to_rgb(hex_code: str) -> RGB_Array_Float:
865
- """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`.
866
1376
 
867
1377
  Parameters
868
1378
  ----------
869
- hex_code : str
870
- A hex string representing a color
1379
+ hex_code
1380
+ A hex string representing a color.
871
1381
 
872
1382
  Returns
873
1383
  -------
874
1384
  RGB_Array_Float
875
- RGB array representing the color
1385
+ An RGB array representing the color.
876
1386
  """
877
1387
  return ManimColor(hex_code).to_rgb()
878
1388
 
879
1389
 
880
1390
  def invert_color(color: ManimColorT) -> ManimColorT:
881
- """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`
882
1393
 
883
1394
  Parameters
884
1395
  ----------
885
- color : ManimColor
886
- A ManimColor
1396
+ color
1397
+ The :class:`ManimColor` to invert.
887
1398
 
888
1399
  Returns
889
1400
  -------
890
1401
  ManimColor
891
- The linearly inverted ManimColor
1402
+ The linearly inverted :class:`ManimColor`.
892
1403
  """
893
1404
  return color.invert()
894
1405
 
895
1406
 
896
- def interpolate_arrays(
897
- arr1: npt.NDArray[Any], arr2: npt.NDArray[Any], alpha: float
898
- ) -> np.ndarray:
899
- """Helper function used in Manim to fade between two objects smoothly
900
-
901
- Parameters
902
- ----------
903
- arr1 : npt.NDArray[Any]
904
- The first array of colors
905
- arr2 : npt.NDArray[Any]
906
- The second array of colors
907
- alpha : float
908
- The alpha value corresponding to the interpolation point between the two inputs
909
-
910
- Returns
911
- -------
912
- np.ndarray
913
- The interpolated value of the to arrays
914
- """
915
- return (1 - alpha) * arr1 + alpha * arr2
916
-
917
-
918
1407
  def color_gradient(
919
1408
  reference_colors: Sequence[ParsableManimColor],
920
1409
  length_of_output: int,
921
1410
  ) -> list[ManimColor] | ManimColor:
922
- """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.
923
1413
 
924
1414
  Parameters
925
1415
  ----------
926
- reference_colors : Sequence[ParsableManimColor]
927
- The colors to be interpolated between or spread apart
928
- length_of_output : int
929
- 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.
930
1420
 
931
1421
  Returns
932
1422
  -------
933
1423
  list[ManimColor] | ManimColor
934
- A list of ManimColor's which has the interpolated colors
1424
+ A :class:`ManimColor` or a list of interpolated :class:`ManimColor`'s.
935
1425
  """
936
1426
  if length_of_output == 0:
937
1427
  return ManimColor(reference_colors[0])
@@ -951,34 +1441,39 @@ def color_gradient(
951
1441
 
952
1442
 
953
1443
  def interpolate_color(
954
- color1: ManimColorT, color2: ManimColor, alpha: float
1444
+ color1: ManimColorT, color2: ManimColorT, alpha: float
955
1445
  ) -> ManimColorT:
956
- """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`.
957
1448
 
958
1449
  Parameters
959
1450
  ----------
960
- color1 : ManimColor
961
- First ManimColor
962
- color2 : ManimColor
963
- Second ManimColor
964
- alpha : float
965
- 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.
966
1457
 
967
1458
  Returns
968
1459
  -------
969
1460
  ManimColor
970
- The interpolated ManimColor
1461
+ The interpolated ManimColor.
971
1462
  """
972
1463
  return color1.interpolate(color2, alpha)
973
1464
 
974
1465
 
975
1466
  def average_color(*colors: ParsableManimColor) -> ManimColor:
976
- """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.
977
1472
 
978
1473
  Returns
979
1474
  -------
980
1475
  ManimColor
981
- The average color of the input
1476
+ The average color of the input.
982
1477
  """
983
1478
  rgbs = np.array([color_to_rgb(color) for color in colors])
984
1479
  mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
@@ -986,31 +1481,31 @@ def average_color(*colors: ParsableManimColor) -> ManimColor:
986
1481
 
987
1482
 
988
1483
  def random_bright_color() -> ManimColor:
989
- """Returns you a random bright color
1484
+ """Return a random bright color: a random color averaged with ``WHITE``.
990
1485
 
991
1486
  .. warning::
992
- 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.
993
1488
 
994
1489
  Returns
995
1490
  -------
996
1491
  ManimColor
997
- A bright ManimColor
1492
+ A random bright :class:`ManimColor`.
998
1493
  """
999
1494
  curr_rgb = color_to_rgb(random_color())
1000
- new_rgb = interpolate_arrays(curr_rgb, np.ones(len(curr_rgb)), 0.5)
1495
+ new_rgb = 0.5 * (curr_rgb + np.ones(3))
1001
1496
  return ManimColor(new_rgb)
1002
1497
 
1003
1498
 
1004
1499
  def random_color() -> ManimColor:
1005
- """Return you a random ManimColor
1500
+ """Return a random :class:`ManimColor`.
1006
1501
 
1007
1502
  .. warning::
1008
- 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.
1009
1504
 
1010
1505
  Returns
1011
1506
  -------
1012
1507
  ManimColor
1013
- _description_
1508
+ A random :class:`ManimColor`.
1014
1509
  """
1015
1510
  import manim.utils.color.manim_colors as manim_colors
1016
1511
 
@@ -1018,17 +1513,38 @@ def random_color() -> ManimColor:
1018
1513
 
1019
1514
 
1020
1515
  def get_shaded_rgb(
1021
- rgb: npt.NDArray[Any],
1022
- point: npt.NDArray[Any],
1023
- unit_normal_vect: npt.NDArray[Any],
1024
- light_source: npt.NDArray[Any],
1025
- ) -> 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
+ """
1026
1542
  to_sun = normalize(light_source - point)
1027
- factor = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3
1028
- if factor < 0:
1029
- factor *= 0.5
1030
- result = rgb + factor
1031
- 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
1032
1548
 
1033
1549
 
1034
1550
  __all__ = [
@@ -1044,11 +1560,12 @@ __all__ = [
1044
1560
  "rgb_to_hex",
1045
1561
  "hex_to_rgb",
1046
1562
  "invert_color",
1047
- "interpolate_arrays",
1048
1563
  "color_gradient",
1049
1564
  "interpolate_color",
1050
1565
  "average_color",
1051
1566
  "random_bright_color",
1052
1567
  "random_color",
1053
1568
  "get_shaded_rgb",
1569
+ "HSV",
1570
+ "RGBA",
1054
1571
  ]