Merge branch 'main' of github.com:ManimCommunity/manim into experimental

This commit is contained in:
Jason G. Villanueva 2023-09-03 18:13:24 -07:00
commit 520577dde3
131 changed files with 8459 additions and 2956 deletions

View file

@ -11,7 +11,7 @@ assignees: ''
- [ ] I have followed the latest version of the
[installation instructions](https://docs.manim.community/en/stable/installation.html).
- [ ] I have checked the [troubleshooting page](https://docs.manim.community/en/stable/installation/troubleshooting.html) and my problem is either not mentioned there,
- [ ] I have checked the [installation FAQ](https://docs.manim.community/en/stable/faq/installation.html) and my problem is either not mentioned there,
or the solution given there does not help.
## Description of error

View file

@ -13,57 +13,6 @@ on:
- main
jobs:
test-arm:
runs-on: self-hosted
env:
DISPLAY: :0
PYTEST_ADDOPTS: "--color=yes" # colors in pytest
strategy:
fail-fast: false
matrix:
python: ["3.8.16", "3.11.1"]
steps:
- name: Checkout the repository
uses: actions/checkout@v3
- name: Check Runner
run: |
which latex
which ffmpeg
latex --version
ffmpeg -version
which python
python --version
- name: Activate Python ${{ matrix.python }}
run: |
echo "/root/.pyenv/versions/${{ matrix.python }}/bin:/root/.local/bin:$PATH" > $GITHUB_PATH
- name: Show Python Version
run: |
python --version --version
- name: Install Manim
run: |
poetry config virtualenvs.prefer-active-python true
poetry install
- name: Run tests
run: |
poetry run pytest
- name: Run module doctests
run: |
poetry run pytest --cov-append --doctest-modules --ignore-glob="*opengl*" manim
- name: Run doctests in rst files
run: |
cd docs && poetry run make doctest O=-tskip-manim
# upload coverage report
- name: Upload coverage
uses: codecov/codecov-action@v3
test:
runs-on: ${{ matrix.os }}
env:
@ -81,7 +30,7 @@ jobs:
- name: Install Poetry
run: |
pipx install poetry
pipx install "poetry==1.5.*"
poetry config virtualenvs.prefer-active-python true
- name: Setup Python ${{ matrix.python }}
@ -119,7 +68,7 @@ jobs:
uses: teatimeguest/setup-texlive-action@v2
with:
cache: true
packages: scheme-basic fontspec inputenc fontenc tipa mathrsfs calligra xcolor standalone preview doublestroke ms everysel setspace rsfs relsize ragged2e fundus-calligra microtype wasysym physics dvisvgm jknapltx wasy cm-super babel-english gnu-freefont mathastext cbfonts-fd
packages: scheme-basic fontspec inputenc fontenc tipa mathrsfs calligra xcolor standalone preview doublestroke ms everysel setspace rsfs relsize ragged2e fundus-calligra microtype wasysym physics dvisvgm jknapltx wasy cm-super babel-english gnu-freefont mathastext cbfonts-fd xetex
- name: Start virtual display (Linux)
if: runner.os == 'Linux'
@ -184,7 +133,7 @@ jobs:
echo "Install Tinytex"
Invoke-WebRequest "https://github.com/yihui/tinytex-releases/releases/download/daily/TinyTeX-1.zip" -O "$($env:TMP)\TinyTex.zip"
Expand-Archive -LiteralPath "$($env:TMP)\TinyTex.zip" -DestinationPath "$($PWD)\ManimCache\LatexWindows"
$env:Path = "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\win32;$($env:PATH)"
$env:Path = "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\windows;$($env:PATH)"
tlmgr update --self
foreach ($c in $tinyTexPackages){
$c=$c.Trim()
@ -196,13 +145,13 @@ jobs:
- name: Add Windows dependencies to PATH
if: runner.os == 'Windows'
run: |
$env:Path += ";" + "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\win32"
$env:Path += ";" + "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\windows"
$env:Path = "$env:USERPROFILE\.poetry\bin;$($env:PATH)"
echo "$env:Path" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install manim
run: |
poetry config experimental.new-installer false
poetry config installer.modern-installation false
poetry install
- name: Run tests

View file

@ -25,7 +25,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
platforms: linux/arm64,linux/amd64
push: true
@ -61,7 +61,7 @@ jobs:
print(f"tag_name={ref_tag}", file=f)
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
platforms: linux/arm64,linux/amd64
push: true

View file

@ -23,6 +23,9 @@ ignore_errors = True
[mypy-manim.utils.*]
ignore_errors = True
[mypy-manim.utils.color]
ignore_errors = False
[mypy-manim.animation.*]
ignore_errors = True

View file

@ -13,7 +13,7 @@ repos:
- id: check-toml
name: Validate Poetry
- repo: https://github.com/pycqa/isort
rev: 5.11.4
rev: 5.12.0
hooks:
- id: isort
name: isort (python)
@ -24,27 +24,27 @@ repos:
name: isort (pyi)
types: [pyi]
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
rev: v3.10.1
hooks:
- id: pyupgrade
name: Update code to new python versions
args: [--py37-plus]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
rev: v1.10.0
hooks:
- id: python-check-blanket-noqa
name: Precision flake ignores
- repo: https://github.com/psf/black
rev: 22.12.0
rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/asottile/blacken-docs
rev: v1.12.1
rev: 1.15.0
hooks:
- id: blacken-docs
additional_dependencies: [black==22.3.0]
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies:
@ -58,7 +58,7 @@ repos:
flake8-simplify==0.14.1,
]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991
rev: v1.4.1
hooks:
- id: mypy
additional_dependencies:
@ -71,7 +71,8 @@ repos:
]
- repo: https://github.com/codespell-project/codespell
rev: v2.2.2
rev: v2.2.5
hooks:
- id: codespell
files: ^.*\.(py|md|rst)$
args: ["-L", "medias,nam"]

View file

@ -1,11 +1,16 @@
version: 2
build:
image: latest
os: ubuntu-22.04
tools:
python: "3.10"
apt_packages:
- libpango1.0-dev
- ffmpeg
- graphviz
python:
version: 3.8
install:
- requirements: docs/rtd-requirements.txt
- requirements: docs/requirements.txt

View file

@ -4,10 +4,10 @@ authors:
-
name: "The Manim Community Developers"
cff-version: "1.2.0"
date-released: 2022-12-26
date-released: 2023-04-06
license: MIT
message: "We acknowledge the importance of good software to support research, and we note that research becomes more valuable when it is communicated effectively. To demonstrate the value of Manim, we ask that you cite Manim in your work."
title: Manim Mathematical Animation Framework
url: "https://www.manim.community/"
version: "v0.17.2"
version: "v0.17.3"
...

View file

@ -171,12 +171,12 @@ msgstr ""
#: ../../../manim/animation/animation.py:docstring of manim.animation.animation.Animation.begin:1:<autosummary>:1
#: ../../../manim/animation/animation.py:docstring of manim.animation.animation.Animation.is_introducer:1
msgid "Test if a the animation is an introducer."
msgid "Test if the animation is an introducer."
msgstr ""
#: ../../../manim/animation/animation.py:docstring of manim.animation.animation.Animation.begin:1:<autosummary>:1
#: ../../../manim/animation/animation.py:docstring of manim.animation.animation.Animation.is_remover:1
msgid "Test if a the animation is a remover."
msgid "Test if the animation is a remover."
msgstr ""
#: ../../../manim/animation/animation.py:docstring of manim.animation.animation.Animation.begin:1:<autosummary>:1

View file

@ -25,7 +25,7 @@ BEGIN {
# The file location of where to put everything
# that has been stripped out
untranslatablefile="./untranslatable.po"
# Wether we are still reading the licence text
# Whether we are still reading the licence text
licencetext=1
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -7,6 +7,8 @@ Qualified name: ``{{ fullname | escape }}``
.. autoclass:: {{ objname }}
:show-inheritance:
:members:
:private-members:
{% block methods %}
{%- if methods %}

View file

@ -5,6 +5,7 @@ Changelog
.. toctree::
changelog/0.17.3-changelog
changelog/0.17.2-changelog
changelog/0.17.1-changelog
changelog/0.17.0-changelog

View file

@ -0,0 +1,207 @@
*******
v0.17.3
*******
:Date: April 06, 2023
Contributors
============
A total of 35 people contributed to this
release. People with a '+' by their names authored a patch for the first
time.
* Alex Lembcke
* Benjamin Hackl
* DegrangeM +
* Elyanah Aco +
* Francisco Manríquez Novoa
* Fredrik Lundström +
* Frédéric Crozatier
* Ikko Eltociear Ashimine +
* ItIsJoeyG +
* JinchuLi2002 +
* Kevin Lubick
* KingAndCross +
* M. A. Ali +
* Matthew Lee +
* Max Coplan +
* Naveen M K
* NotWearingPants
* Oscar Rangel +
* Papierkorb2292 +
* Phoenix2157 +
* Tristan Schulz
* ciobaca +
* coreyp1 +
* davidot +
* icedcoffeeee
* karpfediem +
* vahndi
The patches included in this release have been reviewed by
the following contributors.
* Benjamin Hackl
* Fredrik Lundström
* Frédéric Crozatier
* Hugues Devimeux
* Kevin Lubick
* KingAndCross
* Matthew Lee
* Naveen M K
* Tristan Schulz
* coreyp1
* davidot
* strager
Pull requests merged
====================
A total of 42 pull requests were merged for this release.
Deprecated classes and functions
--------------------------------
* :pr:`3103`: Removed deprecated function ``OpenGLSurface.set_fill_by_value``
New features
------------
* :pr:`2974`: Added :class:`.DiGraph`, a mobject representing directed graphs
* :pr:`3042`: Added :meth:`.Scene.replace` and use in :class:`.ReplacementTransform`
* :pr:`3155`: Added support for individualized radius values in :meth:`.Polygram.round_corners`
* :pr:`3159`: Added :meth:`.set_opacity_by_tex` method for setting the opacity of parts of Tex mobjects
* :pr:`3201`: New tip shape :class:`.StealthTip`, allow specifying tip shape of :class:`.NumberLine`
Enhancements
------------
* :pr:`3046`: Add warning if font is not found for Text, Code, and MarkupText
* :pr:`3083`: Minor performance improvement in :mod:`.bezier` with preallocating array
* :pr:`3092`: Improved :meth:`.Mobject.add` performance by checking for redundancy only once
* :pr:`3134`: Performance: Store color data of ``OpenGLSurface`` to prevent OpenGL embed lag
* :pr:`3180`: Performance: Speed up width/height/depth calculations by reducing copying
* :pr:`3181`: Improved creation time for large :class:`.Text` mobjects
* :pr:`3182`: Reduce memory allocations when building :class:`.SVGMobject`
* :pr:`3191`: Fixed OpenGL rendering in named threads
Fixed bugs
----------
* :pr:`3015`: Fixed bug with ``label_constructor`` in :meth:`.NumberLine.add_labels`
* :pr:`3095`: Fixed ``get_axis_labels`` for :class:`.Axes` and :class:`.ThreeDAxes`
* :pr:`3106`: Fixed ignored ``depth_test`` argument for ``OpenGLVMobjects``
* :pr:`3149`: Allow to use ``call_updater=True`` in :meth:`.Mobject.add_updater` with non-timebased updaters too
* :pr:`3152`: Fixed behavior of :class:`.Wait` and :meth:`.Scene.wait` with specified ``stop_condition``
* :pr:`3163`: Fixed :class:`.BraceLabel` not passing additional keyword arguments to :class:`.Brace`
* :pr:`3195`: Fixed :class:`.Axes` scaling for :meth:`.plot_implicit_curve`
Documentation-related changes
-----------------------------
* :pr:`3105`: Converted types specified in docstrings to proper type hints in :mod:`.three_dimensions`
* :pr:`3108`: Clarified documentation for ``--resolution`` command line flag
* :pr:`3109`: Clean-up, type-hints and documentation for :mod:`.three_dimensions`
* :pr:`3124`: Fixed docstring of :meth:`.ThreeDCamera.get_value_trackers`
* :pr:`3126`: Fixed dead links to troubleshooting page
* :pr:`3137`: Fixed example using ``reverse=True`` with :class:`.Write`
* :pr:`3160`: Fixed a typo
* :pr:`3189`: Corrected the hinted return type for :func:`angle_between_vectors`
* :pr:`3199`: Updated ``winget`` command for installing MiKTeX in documentation
* :pr:`3204`: Fixed docstring formatting of :meth:`.Scene.replace` and improved its error handling
Code quality improvements and similar refactors
-----------------------------------------------
* :pr:`3144`: Fixed typo in ``stripUntranslatable.awk``
* :pr:`3154`: Bump ipython from 8.7.0 to 8.10.0
* :pr:`3156`: CI: Remove actions using self-hosted runners
* :pr:`3164`: Bump markdown-it-py from 2.1.0 to 2.2.0
* :pr:`3165`: Removed deprecated keyword argument in :meth:`.Mobject.align_to`
* :pr:`3166`: Made :class:`.ArrowTriangleTip`, :class:`.ArrowTriangleFilledTip` available to module namespace
* :pr:`3179`: Fixed deprecation warning in :class:`.ParametricFunction` with ``use_vectorized=True``
* :pr:`3186`: Updated extlinks to work with latest version of Sphinx
* :pr:`3196`: CI: updated PATH for recent changed in TinyTex
* :pr:`3200`: Made import from ``moderngl`` compatible with more recent versions
New releases
------------
* :pr:`3198`: Prepare new release: v0.17.3

View file

@ -43,6 +43,7 @@ extensions = [
"sphinx.ext.viewcode",
"sphinxext.opengraph",
"manim.utils.docbuild.manim_directive",
"manim.utils.docbuild.autocolor_directive",
"sphinx.ext.graphviz",
"sphinx.ext.inheritance_diagram",
"sphinxcontrib.programoutput",
@ -138,14 +139,16 @@ html_css_files = ["custom.css"]
# external links
extlinks = {
"issue": ("https://github.com/ManimCommunity/manim/issues/%s", "#"),
"pr": ("https://github.com/ManimCommunity/manim/pull/%s", "#"),
"issue": ("https://github.com/ManimCommunity/manim/issues/%s", "#%s"),
"pr": ("https://github.com/ManimCommunity/manim/pull/%s", "#%s"),
}
# opengraph settings
ogp_image = "https://www.manim.community/logo.png"
ogp_site_name = "Manim Community | Documentation"
ogp_site_url = "https://docs.manim.community/"
ogp_social_cards = {
"image": "_static/logo.png",
}
# inheritance_graph settings

View file

@ -385,7 +385,7 @@ describing the control points in between ("handles").
.. hint::
To learn more about Bézier curves, take a look at the excellent
online textbook `A Primer on Bézier curves <https://pomax.github.io/bezierinfo/>`__
by `Pomax <https://twitter.com/TheRealPomax>`__ -- there is an playground representing
by `Pomax <https://twitter.com/TheRealPomax>`__ -- there is a playground representing
cubic Bézier curves `in §1 <https://pomax.github.io/bezierinfo/#introduction>`__,
the red and yellow points are "anchors", and the green and blue
points are "handles".

View file

@ -59,7 +59,7 @@ in order for Manim to work properly, some additional system
dependencies need to be installed first. The following pages have
operating system specific instructions for you to follow.
Manim requires Python version ``3.7`` or above to run.
Manim requires Python version ``3.8`` or above to run.
.. hint::

View file

@ -5,7 +5,7 @@ The installation instructions depend on your particular operating
system and package manager. If you happen to know exactly what you are doing,
you can also simply ensure that your system has:
- a reasonably recent version of Python 3 (3.73.10),
- a reasonably recent version of Python 3 (3.8 or above),
- with working Cairo bindings in the form of
`pycairo <https://cairographics.org/pycairo/>`__,
- FFmpeg accessible from the command line as ``ffmpeg``,
@ -145,6 +145,12 @@ installed by running:
sudo apt install texlive texlive-latex-extra
For Fedora (see `docs <https://docs.fedoraproject.org/en-US/neurofedora/latex/>`__):
.. code-block:: bash
sudo dnf install texlive-scheme-full
Should you choose to work with some smaller TeX distribution like
`TinyTeX <https://yihui.org/tinytex/>`__ , the full list
of LaTeX packages which Manim interacts with in some way (a subset might

View file

@ -34,7 +34,7 @@ are required, namely:
.. code-block:: bash
brew install pango scipy
brew install pango pkg-config scipy
After all required dependencies are installed, simply run:

View file

@ -6,7 +6,7 @@ package manager like `Chocolatey <https://chocolatey.org/>`__
or `Scoop <https://scoop.sh>`__. If you are not afraid of editing
your System's ``PATH``, a manual installation is also possible.
In fact, if you already have an existing Python
installation (3.7-3.10), it might be the easiest way to get
installation (3.8 or above), it might be the easiest way to get
everything up and running.
If you choose to use one of the package managers, please follow
@ -19,7 +19,7 @@ to make one of them available on your system.
Required Dependencies
---------------------
Manim requires a recent version of Python (3.73.10) and ``ffmpeg``
Manim requires a recent version of Python (3.8 or above) and ``ffmpeg``
in order to work.
Chocolatey
@ -80,10 +80,10 @@ Manual Installation
*******************
As mentioned above, Manim needs a reasonably recent version of
Python 3 (3.73.10) and FFmpeg.
Python 3 (3.8 or above) and FFmpeg.
**Python:** Head over to https://www.python.org, download an installer
for Python (3.73.10), and follow its instructions to get Python
for a recent version of Python, and follow its instructions to get Python
installed on your system.
.. note::
@ -137,7 +137,7 @@ For Windows, the recommended LaTeX distribution is
`MiKTeX <https://miktex.org/download>`__. You can install it by using the
installer from the linked MiKTeX site, or by using the package manager
of your choice (Chocolatey: ``choco install miktex.install``,
Scoop: ``scoop install latex``, Winget: ``winget install ChristianSchenk.MiKTeX``).
Scoop: ``scoop install latex``, Winget: ``winget install MiKTeX.MiKTeX``).
If you are concerned about disk space, there are some alternative,
smaller distributions of LaTeX.

View file

@ -42,6 +42,7 @@ from .constants import *
from .mobject.frame import *
from .mobject.geometry.arc import *
from .mobject.geometry.boolean_ops import *
from .mobject.geometry.labeled import *
from .mobject.geometry.line import *
from .mobject.geometry.polygram import *
from .mobject.geometry.shape_matchers import *

View file

@ -7,6 +7,7 @@ import cloup
from . import __version__, cli_ctx_settings, console
from .cli.cfg.group import cfg
from .cli.checkhealth.commands import checkhealth
from .cli.default_group import DefaultGroup
from .cli.init.commands import init
from .cli.new.group import new
@ -48,6 +49,7 @@ def main(ctx):
pass
main.add_command(checkhealth)
main.add_command(cfg)
main.add_command(plugins)
main.add_command(init)

View file

@ -25,11 +25,11 @@ from collections.abc import Mapping, MutableMapping
from pathlib import Path
from typing import Any, Iterable, Iterator
import colour
import numpy as np
from .. import constants
from ..constants import RendererType
from ..utils.color import ManimColor
from ..utils.tex import TexTemplate, TexTemplateFromFile
from ..utils.tex_templates import TexTemplateLibrary
@ -1098,7 +1098,7 @@ class ManimConfig(MutableMapping):
background_color = property(
lambda self: self._d["background_color"],
lambda self, val: self._d.__setitem__("background_color", colour.Color(val)),
lambda self, val: self._d.__setitem__("background_color", ManimColor(val)),
doc="Background color of the scene (-c).",
)
@ -1174,7 +1174,7 @@ class ManimConfig(MutableMapping):
keys = ["pixel_width", "pixel_height", "frame_rate"]
q = {k: self[k] for k in keys}
for qual in constants.QUALITIES:
if all([q[k] == constants.QUALITIES[qual][k] for k in keys]):
if all(q[k] == constants.QUALITIES[qual][k] for k in keys):
return qual
return None

View file

@ -468,7 +468,7 @@ class Animation:
return self
def is_remover(self) -> bool:
"""Test if a the animation is a remover.
"""Test if the animation is a remover.
Returns
-------
@ -478,7 +478,7 @@ class Animation:
return self.remover
def is_introducer(self) -> bool:
"""Test if a the animation is an introducer.
"""Test if the animation is an introducer.
Returns
-------
@ -539,8 +539,8 @@ class Wait(Animation):
stop_condition
A function without positional arguments that evaluates to a boolean.
The function is evaluated after every new frame has been rendered.
Playing the animation only stops after the return value is truthy.
Overrides the specified ``run_time``.
Playing the animation stops after the return value is truthy, or
after the specified ``run_time`` has passed.
frozen_frame
Controls whether or not the wait animation is static, i.e., corresponds
to a frozen frame. If ``False`` is passed, the render loop still

View file

@ -6,12 +6,17 @@ __all__ = ["AnimatedBoundary", "TracedPath"]
from typing import Callable
from colour import Color
from manim._config import config
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.utils.color import BLUE_B, BLUE_D, BLUE_E, GREY_BROWN, WHITE
from manim.utils.color import (
BLUE_B,
BLUE_D,
BLUE_E,
GREY_BROWN,
WHITE,
ParsableManimColor,
)
from manim.utils.rate_functions import smooth
@ -140,7 +145,7 @@ class TracedPath(VMobject, metaclass=ConvertToOpenGL):
self,
traced_point_func: Callable,
stroke_width: float = 2,
stroke_color: Color = WHITE,
stroke_color: ParsableManimColor | None = WHITE,
dissipating_time: float | None = None,
**kwargs,
):

View file

@ -77,13 +77,13 @@ import itertools as it
from typing import TYPE_CHECKING, Callable, Iterable, Sequence
import numpy as np
from colour import Color
if TYPE_CHECKING:
from manim.mobject.text.text_mobject import Text
from manim.mobject.opengl.opengl_surface import OpenGLSurface
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
from manim.utils.color import ManimColor
from .. import config
from ..animation.animation import Animation
@ -259,7 +259,7 @@ class DrawBorderThenFill(Animation):
sm.set_stroke(color=self.get_stroke_color(sm), width=self.stroke_width)
return outline
def get_stroke_color(self, vmobject: VMobject | OpenGLVMobject) -> Color:
def get_stroke_color(self, vmobject: VMobject | OpenGLVMobject) -> ManimColor:
if self.stroke_color:
return self.stroke_color
elif vmobject.get_stroke_width() > 0:
@ -301,7 +301,7 @@ class Write(DrawBorderThenFill):
class ShowWriteReversed(Scene):
def construct(self):
self.play(Write(Text("Hello", font_size=144), reverse=True))
self.play(Write(Text("Hello", font_size=144), reverse=True, remover=False))
Tests
-----
@ -404,7 +404,6 @@ class Unwrite(Write):
reverse: bool = True,
**kwargs,
) -> None:
run_time: float | None = kwargs.pop("run_time", None)
lag_ratio: float | None = kwargs.pop("lag_ratio", None)
run_time, lag_ratio = self._set_default_config_from_length(

View file

@ -40,7 +40,6 @@ __all__ = [
from typing import Callable, Iterable, Optional, Tuple, Type, Union
import numpy as np
from colour import Color
from manim.mobject.geometry.arc import Circle, Dot
from manim.mobject.geometry.line import Line
@ -58,7 +57,7 @@ from ..constants import *
from ..mobject.mobject import Mobject
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..utils.bezier import interpolate, inverse_interpolate
from ..utils.color import GREY, YELLOW
from ..utils.color import GREY, YELLOW, ParsableManimColor
from ..utils.deprecation import deprecated
from ..utils.rate_functions import smooth, there_and_back, wiggle
from ..utils.space_ops import normalize
@ -609,7 +608,7 @@ class Circumscribe(Succession):
fade_out=False,
time_width=0.3,
buff: float = SMALL_BUFF,
color: Color = YELLOW,
color: ParsableManimColor = YELLOW,
run_time=1,
stroke_width=DEFAULT_STROKE_WIDTH,
**kwargs

View file

@ -97,7 +97,6 @@ class ChangeSpeed(Animation):
affects_speed_updaters: bool = True,
**kwargs,
) -> None:
if issubclass(type(anim), AnimationGroup):
self.anim = type(anim)(
*map(self.setup, anim.animations),

View file

@ -208,8 +208,7 @@ class Transform(Animation):
def clean_up_from_scene(self, scene: Scene) -> None:
super().clean_up_from_scene(scene)
if self.replace_mobject_with_target_in_scene:
scene.remove(self.mobject)
scene.add(self.target_mobject)
scene.replace(self.mobject, self.target_mobject)
def get_all_mobjects(self) -> Sequence[Mobject]:
return [

View file

@ -76,7 +76,6 @@ class TransformMatchingAbstractBase(AnimationGroup):
key_map: dict | None = None,
**kwargs,
):
if isinstance(mobject, OpenGLVMobject):
group_type = OpenGLVGroup
elif isinstance(mobject, OpenGLMobject):

View file

@ -15,6 +15,7 @@ __all__ = [
import inspect
from collections.abc import Callable
import numpy as np
@ -55,9 +56,46 @@ def f_always(method, *arg_generators, **kwargs):
return mobject
def always_redraw(func):
def always_redraw(func: Callable[[], Mobject]) -> Mobject:
"""Redraw the mobject constructed by a function every frame.
This function returns a mobject with an attached updater that
continuously regenerates the mobject according to the
specified function.
Parameters
----------
func
A function without (required) input arguments that returns
a mobject.
Examples
--------
.. manim:: TangentAnimation
class TangentAnimation(Scene):
def construct(self):
ax = Axes()
sine = ax.plot(np.sin, color=RED)
alpha = ValueTracker(0)
point = always_redraw(
lambda: Dot(
sine.point_from_proportion(alpha.get_value()),
color=BLUE)
)
tangent = always_redraw(
lambda: TangentLine(
sine,
alpha=alpha.get_value(),
color=YELLOW,
length=4)
)
self.add(ax, sine, point, tangent)
self.play(alpha.animate.set_value(1), rate_func=linear, run_time=2)
"""
mob = func()
mob.add_updater(lambda m: mob.become(func()))
mob.add_updater(lambda _: mob.become(func()))
return mob

View file

View file

@ -0,0 +1,173 @@
"""Auxiliary module for the checkhealth subcommand, contains
the actual check implementations."""
from __future__ import annotations
import os
import shutil
import subprocess
from typing import Callable
from ..._config import config
HEALTH_CHECKS = []
def healthcheck(
description: str,
recommendation: str,
skip_on_failed: list[Callable | str] | None = None,
post_fail_fix_hook: Callable | None = None,
):
"""Decorator used for declaring health checks.
This decorator attaches some data to a function,
which is then added to a list containing all checks.
Parameters
----------
description
A brief description of this check, displayed when
the checkhealth subcommand is run.
recommendation
Help text which is displayed in case the check fails.
skip_on_failed
A list of check functions which, if they fail, cause
the current check to be skipped.
post_fail_fix_hook
A function that is supposed to (interactively) help
to fix the detected problem, if possible. This is
only called upon explicit confirmation of the user.
Returns
-------
A check function, as required by the checkhealth subcommand.
"""
if skip_on_failed is None:
skip_on_failed = []
skip_on_failed = [
skip.__name__ if callable(skip) else skip for skip in skip_on_failed
]
def decorator(func):
func.description = description
func.recommendation = recommendation
func.skip_on_failed = skip_on_failed
func.post_fail_fix_hook = post_fail_fix_hook
HEALTH_CHECKS.append(func)
return func
return decorator
@healthcheck(
description="Checking whether manim is on your PATH",
recommendation=(
"The command <manim> is currently not on your system's PATH.\n\n"
"You can work around this by calling the manim module directly "
"via <python -m manim> instead of just <manim>.\n\n"
"To fix the PATH issue properly: "
"Usually, the Python package installer pip issues a warning "
"during the installation which contains more information. "
"Consider reinstalling manim via <pip uninstall manim> "
"followed by <pip install manim> to see the warning again, "
"then consult the internet on how to modify your system's "
"PATH variable."
),
)
def is_manim_on_path():
path_to_manim = shutil.which("manim")
return path_to_manim is not None
@healthcheck(
description="Checking whether the executable belongs to manim",
recommendation=(
"The command <manim> does not belong to your installed version "
"of this library, it likely belongs to manimgl / manimlib.\n\n"
"Run manim via <python -m manim> or via <manimce>, or uninstall "
"and reinstall manim via <pip install --upgrade "
"--force-reinstall manim> to fix this."
),
skip_on_failed=[is_manim_on_path],
)
def is_manim_executable_associated_to_this_library():
path_to_manim = shutil.which("manim")
with open(path_to_manim, "rb") as f:
manim_exec = f.read()
# first condition below corresponds to the executable being
# some sort of python script. second condition happens when
# the executable is actually a Windows batch file.
return b"manim.__main__" in manim_exec or b'"%~dp0\\manim"' in manim_exec
@healthcheck(
description="Checking whether ffmpeg is available",
recommendation=(
"Manim does not work without ffmpeg. Please follow our "
"installation instructions "
"at https://docs.manim.community/en/stable/installation.html "
"to download ffmpeg. Then, either ...\n\n"
"(a) ... make the ffmpeg executable available to your system's PATH,\n"
"(b) or, alternatively, use <manim cfg write --open> to create a "
"custom configuration and set the ffmpeg_executable variable to the "
"full absolute path to the ffmpeg executable."
),
)
def is_ffmpeg_available():
path_to_ffmpeg = shutil.which(config.ffmpeg_executable)
return path_to_ffmpeg is not None and os.access(path_to_ffmpeg, os.X_OK)
@healthcheck(
description="Checking whether ffmpeg is working",
recommendation=(
"Your installed version of ffmpeg does not support x264 encoding, "
"which manim requires. Please follow our installation instructions "
"at https://docs.manim.community/en/stable/installation.html "
"to download and install a newer version of ffmpeg."
),
skip_on_failed=[is_ffmpeg_available],
)
def is_ffmpeg_working():
ffmpeg_version = subprocess.run(
[config.ffmpeg_executable, "-version"],
stdout=subprocess.PIPE,
).stdout.decode()
return (
ffmpeg_version.startswith("ffmpeg version")
and "--enable-libx264" in ffmpeg_version
)
@healthcheck(
description="Checking whether latex is available",
recommendation=(
"Manim cannot find <latex> on your system's PATH. "
"You will not be able to use Tex and MathTex mobjects "
"in your scenes.\n\n"
"Consult our installation instructions "
"at https://docs.manim.community/en/stable/installation.html "
"or search the web for instructions on how to install a "
"LaTeX distribution on your operating system."
),
)
def is_latex_available():
path_to_latex = shutil.which("latex")
return path_to_latex is not None and os.access(path_to_latex, os.X_OK)
@healthcheck(
description="Checking whether dvisvgm is available",
recommendation=(
"Manim could find <latex>, but not <dvisvgm> on your system's "
"PATH. Make sure your installed LaTeX distribution comes with "
"dvisvgm and consider installing a larger distribution if it "
"does not."
),
skip_on_failed=[is_latex_available],
)
def is_dvisvgm_available():
path_to_dvisvgm = shutil.which("dvisvgm")
return path_to_dvisvgm is not None and os.access(path_to_dvisvgm, os.X_OK)

View file

@ -0,0 +1,81 @@
"""A CLI utility helping to diagnose problems with
your Manim installation.
"""
from __future__ import annotations
import sys
import click
import cloup
from .checks import HEALTH_CHECKS
@cloup.command(
context_settings=None,
)
def checkhealth():
"""This subcommand checks whether Manim is installed correctly
and has access to its required (and optional) system dependencies.
"""
click.echo(f"Python executable: {sys.executable}\n")
click.echo("Checking whether your installation of Manim Community is healthy...")
failed_checks = []
for check in HEALTH_CHECKS:
click.echo(f"- {check.description} ... ", nl=False)
if any(
failed_check.__name__ in check.skip_on_failed
for failed_check in failed_checks
):
click.secho("SKIPPED", fg="blue")
continue
check_result = check()
if check_result:
click.secho("PASSED", fg="green")
else:
click.secho("FAILED", fg="red")
failed_checks.append(check)
click.echo()
if failed_checks:
click.echo(
"There are problems with your installation, "
"here are some recommendations to fix them:"
)
for ind, failed_check in enumerate(failed_checks):
click.echo(failed_check.recommendation)
if ind + 1 < len(failed_checks):
click.confirm("Continue with next recommendation?")
else: # no problems detected!
click.echo("No problems detected, your installation seems healthy!")
render_test_scene = click.confirm(
"Would you like to render and preview a test scene?"
)
if render_test_scene:
import manim as mn
class CheckHealthDemo(mn.Scene):
def construct(self):
banner = mn.ManimBanner().shift(mn.UP * 0.5)
self.play(banner.create())
self.wait(0.5)
self.play(banner.expand())
self.wait(0.5)
text_left = mn.Text("All systems operational!")
formula_right = mn.MathTex(r"\oint_{\gamma} f(z)~dz = 0")
text_tex_group = mn.VGroup(text_left, formula_right)
text_tex_group.arrange(mn.RIGHT, buff=1).next_to(banner, mn.DOWN)
self.play(mn.Write(text_tex_group))
self.wait(0.5)
self.play(
mn.FadeOut(banner, shift=mn.UP),
mn.FadeOut(text_tex_group, shift=mn.DOWN),
)
with mn.tempconfig({"preview": True, "disable_caching": True}):
CheckHealthDemo().render()

View file

@ -1,9 +0,0 @@
"""The colormap of manim community"""
from __future__ import annotations
LOGO_WHITE = "#ece6e2"
LOGO_GREEN = "#87c2a5"
LOGO_BLUE = "#525893"
LOGO_RED = "#e07a5f"
LOGO_BLACK = "#343434"

View file

@ -62,6 +62,7 @@ __all__ = [
"DEFAULT_POINT_DENSITY_1D",
"DEFAULT_STROKE_WIDTH",
"DEFAULT_FONT_SIZE",
"SCALE_FACTOR_PER_FONT_POINT",
"PI",
"TAU",
"DEGREES",
@ -181,6 +182,7 @@ DEFAULT_POINT_DENSITY_2D: int = 25
DEFAULT_POINT_DENSITY_1D: int = 10
DEFAULT_STROKE_WIDTH: int = 4
DEFAULT_FONT_SIZE: float = 48
SCALE_FACTOR_PER_FONT_POINT: float = 1 / 960
# Mathematical constants
PI: float = np.pi

View file

@ -8,6 +8,7 @@ Modules
~arc
~boolean_ops
~labeled
~line
~polygram
~shape_matchers

View file

@ -48,7 +48,6 @@ import warnings
from typing import TYPE_CHECKING, Sequence
import numpy as np
from colour import Color
from manim.constants import *
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
@ -282,8 +281,8 @@ class Arc(TipableVMobject):
def __init__(
self,
radius: float = 1.0,
start_angle=0,
angle=TAU / 4,
start_angle: float = 0,
angle: float = TAU / 4,
num_components=9,
arc_center=ORIGIN,
**kwargs,
@ -492,7 +491,7 @@ class Circle(Arc):
def __init__(
self,
radius: float | None = None,
color: Color | str = RED,
color: ParsableManimColor = RED,
**kwargs,
):
super().__init__(
@ -658,7 +657,7 @@ class Dot(Circle):
radius: float = DEFAULT_DOT_RADIUS,
stroke_width: float = 0,
fill_opacity: float = 1.0,
color: Color | str = WHITE,
color: ParsableManimColor = WHITE,
**kwargs,
):
super().__init__(
@ -777,7 +776,9 @@ class Ellipse(Circle):
class AnnularSector(Arc):
"""
"""A sector of an annulus.
Parameters
----------
inner_radius
@ -862,7 +863,8 @@ class AnnularSector(Arc):
class Sector(AnnularSector):
"""
"""A sector of a circle.
Examples
--------
.. manim:: ExampleSector
@ -935,7 +937,8 @@ class Annulus(Circle):
class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
"""
"""A cubic Bézier curve.
Example
-------
.. manim:: BezierSplineExample

View file

@ -0,0 +1,155 @@
r"""Mobjects that inherit from lines and contain a label along the length."""
from __future__ import annotations
__all__ = ["LabeledLine", "LabeledArrow"]
from manim.constants import *
from manim.mobject.geometry.line import Arrow, Line
from manim.mobject.geometry.shape_matchers import (
BackgroundRectangle,
SurroundingRectangle,
)
from manim.mobject.text.tex_mobject import MathTex, Tex
from manim.mobject.text.text_mobject import Text
from manim.utils.color import WHITE, ManimColor, ParsableManimColor
class LabeledLine(Line):
"""Constructs a line containing a label box somewhere along its length.
Parameters
----------
label : str | Tex | MathTex | Text
Label that will be displayed on the line.
label_position : float | optional
A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5.
font_size : float | optional
Control font size for the label. This parameter is only used when `label` is of type `str`.
label_color: ParsableManimColor | optional
The color of the label's text. This parameter is only used when `label` is of type `str`.
label_frame : Bool | optional
Add a `SurroundingRectangle` frame to the label box.
frame_fill_color : ParsableManimColor | optional
Background color to fill the label box. If no value is provided, the background color of the canvas will be used.
frame_fill_opacity : float | optional
Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity.
.. seealso::
:class:`LabeledArrow`
Examples
--------
.. manim:: LabeledLineExample
:save_last_frame:
class LabeledLineExample(Scene):
def construct(self):
line = LabeledLine(
label = '0.5',
label_position = 0.8,
font_size = 20,
label_color = WHITE,
label_frame = True,
start=LEFT+DOWN,
end=RIGHT+UP)
line.set_length(line.get_length() * 2)
self.add(line)
"""
def __init__(
self,
label: str | Tex | MathTex | Text,
label_position: float = 0.5,
font_size: float = DEFAULT_FONT_SIZE,
label_color: ParsableManimColor = WHITE,
label_frame: bool = True,
frame_fill_color: ParsableManimColor = None,
frame_fill_opacity: float = 1,
*args,
**kwargs,
) -> None:
label_color = ManimColor(label_color)
frame_fill_color = ManimColor(frame_fill_color)
if isinstance(label, str):
from manim import MathTex
rendered_label = MathTex(label, color=label_color, font_size=font_size)
else:
rendered_label = label
super().__init__(*args, **kwargs)
# calculating the vector for the label position
line_start, line_end = self.get_start_and_end()
new_vec = (line_end - line_start) * label_position
label_coords = line_start + new_vec
# rendered_label.move_to(self.get_vector() * label_position)
rendered_label.move_to(label_coords)
box = BackgroundRectangle(
rendered_label,
buff=0.05,
color=frame_fill_color,
fill_opacity=frame_fill_opacity,
stroke_width=0.5,
)
self.add(box)
if label_frame:
box_frame = SurroundingRectangle(
rendered_label, buff=0.05, color=label_color, stroke_width=0.5
)
self.add(box_frame)
self.add(rendered_label)
class LabeledArrow(LabeledLine, Arrow):
"""Constructs an arrow containing a label box somewhere along its length.
This class inherits its label properties from `LabeledLine`, so the main parameters controlling it are the same.
Parameters
----------
label : str | Tex | MathTex | Text
Label that will be displayed on the line.
label_position : float | optional
A ratio in the range [0-1] to indicate the position of the label with respect to the length of the line. Default value is 0.5.
font_size : float | optional
Control font size for the label. This parameter is only used when `label` is of type `str`.
label_color: ParsableManimColor | optional
The color of the label's text. This parameter is only used when `label` is of type `str`.
label_frame : Bool | optional
Add a `SurroundingRectangle` frame to the label box.
frame_fill_color : ParsableManimColor | optional
Background color to fill the label box. If no value is provided, the background color of the canvas will be used.
frame_fill_opacity : float | optional
Determine the opacity of the label box by passing a value in the range [0-1], where 0 indicates complete transparency and 1 means full opacity.
.. seealso::
:class:`LabeledLine`
Examples
--------
.. manim:: LabeledArrowExample
:save_last_frame:
class LabeledArrowExample(Scene):
def construct(self):
l_arrow = LabeledArrow("0.5", start=LEFT*3, end=RIGHT*3 + UP*2, label_position=0.5)
self.add(l_arrow)
"""
def __init__(
self,
*args,
**kwargs,
) -> None:
super().__init__(*args, **kwargs)

View file

@ -17,7 +17,6 @@ __all__ = [
from typing import Any, Sequence
import numpy as np
from colour import Color
from manim import config
from manim.constants import *
@ -28,7 +27,6 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import DashedVMobject, VGroup, VMobject
from manim.utils.color import *
from manim.utils.color import Colors
from manim.utils.space_ops import angle_of_vector, line_intersection, normalize
@ -641,7 +639,7 @@ class Vector(Arrow):
self,
integer_labels: bool = True,
n_dim: int = 2,
color: Color | None = None,
color: ParsableManimColor | None = None,
**kwargs,
):
"""Creates a label based on the coordinates of the vector.
@ -873,7 +871,7 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
dot: bool = False,
dot_radius: float | None = None,
dot_distance: float = 0.55,
dot_color: Colors = WHITE,
dot_color: ParsableManimColor = WHITE,
elbow: bool = False,
**kwargs,
):

View file

@ -15,10 +15,10 @@ __all__ = [
"Cutout",
]
from math import ceil
from typing import Iterable, Sequence
import numpy as np
from colour import Color
from manim.constants import *
from manim.mobject.geometry.arc import ArcBetweenPoints
@ -133,18 +133,52 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
return np.array(vertex_groups)
def round_corners(self, radius: float = 0.5):
def round_corners(
self,
radius: float | list[float] = 0.5,
evenly_distribute_anchors: bool = False,
components_per_rounded_corner: int = 2,
):
"""Rounds off the corners of the :class:`Polygram`.
Parameters
----------
radius
The curvature of the corners of the :class:`Polygram`.
evenly_distribute_anchors
Break long line segments into proportionally-sized segments.
components_per_rounded_corner
The number of points used to represent the rounded corner curve.
.. seealso::
:class:`.~RoundedRectangle`
.. note::
If `radius` is supplied as a single value, then the same radius
will be applied to all corners. If `radius` is a list, then the
individual values will be applied sequentially, with the first
corner receiving `radius[0]`, the second corner receiving
`radius[1]`, etc. The radius list will be repeated as necessary.
The `components_per_rounded_corner` value is provided so that the
fidelity of the rounded corner may be fine-tuned as needed. 2 is
an appropriate value for most shapes, however a larger value may be
need if the rounded corner is particularly large. 2 is the minimum
number allowed, representing the start and end of the curve. 3 will
result in a start, middle, and end point, meaning 2 curves will be
generated.
The option to `evenly_distribute_anchors` is provided so that the
line segments (the part part of each line remaining after rounding
off the corners) can be subdivided to a density similar to that of
the average density of the rounded corners. This may be desirable
in situations in which an even distribution of curves is desired
for use in later transformation animations. Be aware, though, that
enabling this option can result in an an object containing
significantly more points than the original, especially when the
rounded corner curves are small.
Examples
--------
.. manim:: PolygramRoundCorners
@ -169,7 +203,17 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
for vertices in self.get_vertex_groups():
arcs = []
for v1, v2, v3 in adjacent_n_tuples(vertices, 3):
# Repeat the radius list as necessary in order to provide a radius
# for each vertex.
if isinstance(radius, (int, float)):
radius_list = [radius] * len(vertices)
else:
radius_list = radius * ceil(len(vertices) / len(radius))
for currentRadius, (v1, v2, v3) in zip(
radius_list, adjacent_n_tuples(vertices, 3)
):
vect1 = v2 - v1
vect2 = v3 - v2
unit_vect1 = normalize(vect1)
@ -177,10 +221,10 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
angle = angle_between_vectors(vect1, vect2)
# Negative radius gives concave curves
angle *= np.sign(radius)
angle *= np.sign(currentRadius)
# Distance between vertex and start of the arc
cut_off_length = radius * np.tan(angle / 2)
cut_off_length = currentRadius * np.tan(angle / 2)
# Determines counterclockwise vs. clockwise
sign = np.sign(np.cross(vect1, vect2)[2])
@ -189,9 +233,24 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
v2 - unit_vect1 * cut_off_length,
v2 + unit_vect2 * cut_off_length,
angle=sign * angle,
num_components=components_per_rounded_corner,
)
arcs.append(arc)
if evenly_distribute_anchors:
# Determine the average length of each curve
nonZeroLengthArcs = [arc for arc in arcs if len(arc.points) > 4]
if len(nonZeroLengthArcs):
totalArcLength = sum(
[arc.get_arc_length() for arc in nonZeroLengthArcs]
)
totalCurveCount = (
sum([len(arc.points) for arc in nonZeroLengthArcs]) / 4
)
averageLengthPerCurve = totalArcLength / totalCurveCount
else:
averageLengthPerCurve = 1
# To ensure that we loop through starting with last
arcs = [arcs[-1], *arcs[:-1]]
from manim.mobject.geometry.line import Line
@ -201,10 +260,11 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
line = Line(arc1.get_end(), arc2.get_start())
# Make sure anchors are evenly distributed
len_ratio = line.get_length() / arc1.get_arc_length()
line.insert_n_curves(int(arc1.get_num_curves() * len_ratio))
# Make sure anchors are evenly distributed, if necessary
if evenly_distribute_anchors:
line.insert_n_curves(
ceil(line.get_length() / averageLengthPerCurve)
)
new_points.extend(line.points)
@ -536,7 +596,7 @@ class Rectangle(Polygon):
def __init__(
self,
color: Color = WHITE,
color: ParsableManimColor = WHITE,
height: float = 2.0,
width: float = 4.0,
grid_xstep: float | None = None,
@ -633,7 +693,7 @@ class RoundedRectangle(Rectangle):
self.add(rect_group)
"""
def __init__(self, corner_radius: float = 0.5, **kwargs):
def __init__(self, corner_radius: float | list[float] = 0.5, **kwargs):
super().__init__(**kwargs)
self.corner_radius = corner_radius
self.round_corners(self.corner_radius)

View file

@ -10,7 +10,7 @@ from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import RoundedRectangle
from manim.mobject.mobject import Mobject
from manim.mobject.types.vectorized_mobject import VGroup
from manim.utils.color import BLACK, RED, YELLOW, Color, Colors
from manim.utils.color import BLACK, RED, YELLOW, ParsableManimColor
class SurroundingRectangle(RoundedRectangle):
@ -79,7 +79,7 @@ class BackgroundRectangle(SurroundingRectangle):
def __init__(
self,
mobject,
color: Colors | None = None,
color: ParsableManimColor | None = None,
stroke_width: float = 0,
stroke_opacity: float = 0,
fill_opacity: float = 0.75,
@ -121,7 +121,7 @@ class BackgroundRectangle(SurroundingRectangle):
return self
def get_fill_color(self):
return Color(self.color)
return self.color
class Cross(VGroup):
@ -152,7 +152,7 @@ class Cross(VGroup):
def __init__(
self,
mobject: Mobject | None = None,
stroke_color: Color = RED,
stroke_color: ParsableManimColor = RED,
stroke_width: float = 6,
scale_factor: float = 1,
**kwargs,

View file

@ -8,6 +8,9 @@ __all__ = [
"ArrowCircleTip",
"ArrowSquareTip",
"ArrowSquareFilledTip",
"ArrowTriangleTip",
"ArrowTriangleFilledTip",
"StealthTip",
]
import numpy as np
@ -30,6 +33,7 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
:class:`ArrowCircleFilledTip`
:class:`ArrowSquareTip`
:class:`ArrowSquareFilledTip`
:class:`StealthTip`
Examples
--------
@ -72,23 +76,34 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
.. manim:: ArrowTipsShowcase
:save_last_frame:
from manim.mobject.geometry.tips import ArrowTriangleTip,\
ArrowSquareTip, ArrowSquareFilledTip,\
ArrowCircleTip, ArrowCircleFilledTip
class ArrowTipsShowcase(Scene):
def construct(self):
a00 = Arrow(start=[-2, 3, 0], end=[2, 3, 0], color=YELLOW)
a11 = Arrow(start=[-2, 2, 0], end=[2, 2, 0], tip_shape=ArrowTriangleTip)
a12 = Arrow(start=[-2, 1, 0], end=[2, 1, 0])
a21 = Arrow(start=[-2, 0, 0], end=[2, 0, 0], tip_shape=ArrowSquareTip)
a22 = Arrow([-2, -1, 0], [2, -1, 0], tip_shape=ArrowSquareFilledTip)
a31 = Arrow([-2, -2, 0], [2, -2, 0], tip_shape=ArrowCircleTip)
a32 = Arrow([-2, -3, 0], [2, -3, 0], tip_shape=ArrowCircleFilledTip)
b11 = a11.copy().scale(0.5, scale_tips=True).next_to(a11, RIGHT)
b12 = a12.copy().scale(0.5, scale_tips=True).next_to(a12, RIGHT)
b21 = a21.copy().scale(0.5, scale_tips=True).next_to(a21, RIGHT)
self.add(a00, a11, a12, a21, a22, a31, a32, b11, b12, b21)
tip_names = [
'Default (YELLOW)', 'ArrowTriangleTip', 'Default', 'ArrowSquareTip',
'ArrowSquareFilledTip', 'ArrowCircleTip', 'ArrowCircleFilledTip', 'StealthTip'
]
big_arrows = [
Arrow(start=[-4, 3.5, 0], end=[2, 3.5, 0], color=YELLOW),
Arrow(start=[-4, 2.5, 0], end=[2, 2.5, 0], tip_shape=ArrowTriangleTip),
Arrow(start=[-4, 1.5, 0], end=[2, 1.5, 0]),
Arrow(start=[-4, 0.5, 0], end=[2, 0.5, 0], tip_shape=ArrowSquareTip),
Arrow([-4, -0.5, 0], [2, -0.5, 0], tip_shape=ArrowSquareFilledTip),
Arrow([-4, -1.5, 0], [2, -1.5, 0], tip_shape=ArrowCircleTip),
Arrow([-4, -2.5, 0], [2, -2.5, 0], tip_shape=ArrowCircleFilledTip),
Arrow([-4, -3.5, 0], [2, -3.5, 0], tip_shape=StealthTip)
]
small_arrows = (
arrow.copy().scale(0.5, scale_tips=True).next_to(arrow, RIGHT) for arrow in big_arrows
)
labels = (
Text(tip_names[i], font='monospace', font_size=20, color=BLUE).next_to(big_arrows[i], LEFT) for i in range(len(big_arrows))
)
self.add(*big_arrows, *small_arrows, *labels)
"""
def __init__(self, *args, **kwargs):
@ -177,6 +192,47 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
return np.linalg.norm(self.vector)
class StealthTip(ArrowTip):
r"""'Stealth' fighter / kite arrow shape.
Naming is inspired by the corresponding
`TikZ arrow shape <https://tikz.dev/tikz-arrows#sec-16.3>`__.
"""
def __init__(
self,
fill_opacity=1,
stroke_width=3,
length=DEFAULT_ARROW_TIP_LENGTH / 2,
start_angle=PI,
**kwargs,
):
self.start_angle = start_angle
VMobject.__init__(
self, fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs
)
self.set_points_as_corners(
[
[2, 0, 0], # tip
[-1.2, 1.6, 0],
[0, 0, 0], # base
[-1.2, -1.6, 0],
[2, 0, 0], # close path, back to tip
]
)
self.scale(length / self.length)
@property
def length(self):
"""The length of the arrow tip.
In this case, the length is computed as the height of
the triangle encompassing the stealth tip (otherwise,
the tip is scaled too large).
"""
return np.linalg.norm(self.vector) * 1.6
class ArrowTriangleTip(ArrowTip, Triangle):
r"""Triangular arrow tip."""

View file

@ -4,6 +4,7 @@ from __future__ import annotations
__all__ = [
"Graph",
"DiGraph",
]
import itertools as it
@ -13,21 +14,20 @@ from typing import Hashable, Iterable
import networkx as nx
import numpy as np
from manim.animation.composition import AnimationGroup
from manim.animation.creation import Create, Uncreate
from manim.mobject.geometry.arc import Dot, LabeledDot
from manim.mobject.geometry.line import Line
from manim.mobject.mobject import Mobject, override_animate
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.text.tex_mobject import MathTex
from ..animation.composition import AnimationGroup
from ..animation.creation import Create, Uncreate
from ..utils.color import BLACK
from .mobject import Mobject, override_animate
from .types.vectorized_mobject import VMobject
from manim.mobject.types.vectorized_mobject import VMobject
from manim.utils.color import BLACK
def _determine_graph_layout(
nx_graph: nx.classes.graph.Graph,
nx_graph: nx.classes.graph.Graph | nx.classes.digraph.DiGraph,
layout: str | dict = "spring",
layout_scale: float = 2,
layout_config: dict | None = None,
@ -106,7 +106,7 @@ def _determine_graph_layout(
def _tree_layout(
T: nx.classes.graph.Graph,
T: nx.classes.graph.Graph | nx.classes.digraph.DiGraph,
root_vertex: Hashable | None,
scale: float | tuple | None = 2,
vertex_spacing: tuple | None = None,
@ -213,18 +213,27 @@ def _tree_layout(
return {v: (np.array([x, y, 0]) - center) * sf for v, (x, y) in pos.items()}
class Graph(VMobject, metaclass=ConvertToOpenGL):
"""An undirected graph (that is, a collection of vertices connected with edges).
class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
"""Abstract base class for graphs (that is, a collection of vertices
connected with edges).
Graphs can be instantiated by passing both a list of (distinct, hashable)
vertex names, together with list of edges (as tuples of vertex names). See
the examples below for details.
the examples for concrete implementations of this class for details.
.. note::
This implementation uses updaters to make the edges move with
the vertices.
See also
--------
:class:`.Graph`
:class:`.DiGraph`
Parameters
----------
@ -285,211 +294,6 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
to the class specified via ``edge_type``, or a dictionary whose
keys are the edges, and whose values are dictionaries containing
keyword arguments for the mobject related to the corresponding edge.
Examples
--------
First, we create a small graph and demonstrate that the edges move
together with the vertices.
.. manim:: MovingVertices
class MovingVertices(Scene):
def construct(self):
vertices = [1, 2, 3, 4]
edges = [(1, 2), (2, 3), (3, 4), (1, 3), (1, 4)]
g = Graph(vertices, edges)
self.play(Create(g))
self.wait()
self.play(g[1].animate.move_to([1, 1, 0]),
g[2].animate.move_to([-1, 1, 0]),
g[3].animate.move_to([1, -1, 0]),
g[4].animate.move_to([-1, -1, 0]))
self.wait()
There are several automatic positioning algorithms to choose from:
.. manim:: GraphAutoPosition
:save_last_frame:
class GraphAutoPosition(Scene):
def construct(self):
vertices = [1, 2, 3, 4, 5, 6, 7, 8]
edges = [(1, 7), (1, 8), (2, 3), (2, 4), (2, 5),
(2, 8), (3, 4), (6, 1), (6, 2),
(6, 3), (7, 2), (7, 4)]
autolayouts = ["spring", "circular", "kamada_kawai",
"planar", "random", "shell",
"spectral", "spiral"]
graphs = [Graph(vertices, edges, layout=lt).scale(0.5)
for lt in autolayouts]
r1 = VGroup(*graphs[:3]).arrange()
r2 = VGroup(*graphs[3:6]).arrange()
r3 = VGroup(*graphs[6:]).arrange()
self.add(VGroup(r1, r2, r3).arrange(direction=DOWN))
Vertices can also be positioned manually:
.. manim:: GraphManualPosition
:save_last_frame:
class GraphManualPosition(Scene):
def construct(self):
vertices = [1, 2, 3, 4]
edges = [(1, 2), (2, 3), (3, 4), (4, 1)]
lt = {1: [0, 0, 0], 2: [1, 1, 0], 3: [1, -1, 0], 4: [-1, 0, 0]}
G = Graph(vertices, edges, layout=lt)
self.add(G)
The vertices in graphs can be labeled, and configurations for vertices
and edges can be modified both by default and for specific vertices and
edges.
.. note::
In ``edge_config``, edges can be passed in both directions: if
``(u, v)`` is an edge in the graph, both ``(u, v)`` as well
as ``(v, u)`` can be used as keys in the dictionary.
.. manim:: LabeledModifiedGraph
:save_last_frame:
class LabeledModifiedGraph(Scene):
def construct(self):
vertices = [1, 2, 3, 4, 5, 6, 7, 8]
edges = [(1, 7), (1, 8), (2, 3), (2, 4), (2, 5),
(2, 8), (3, 4), (6, 1), (6, 2),
(6, 3), (7, 2), (7, 4)]
g = Graph(vertices, edges, layout="circular", layout_scale=3,
labels=True, vertex_config={7: {"fill_color": RED}},
edge_config={(1, 7): {"stroke_color": RED},
(2, 7): {"stroke_color": RED},
(4, 7): {"stroke_color": RED}})
self.add(g)
You can also lay out a partite graph on columns by specifying
a list of the vertices on each side and choosing the partite layout.
.. note::
All vertices in your graph which are not listed in any of the partitions
are collected in their own partition and rendered in the rightmost column.
.. manim:: PartiteGraph
:save_last_frame:
import networkx as nx
class PartiteGraph(Scene):
def construct(self):
G = nx.Graph()
G.add_nodes_from([0, 1, 2, 3])
G.add_edges_from([(0, 2), (0,3), (1, 2)])
graph = Graph(list(G.nodes), list(G.edges), layout="partite", partitions=[[0, 1]])
self.play(Create(graph))
The representation of a linear artificial neural network is facilitated
by the use of the partite layout and defining partitions for each layer.
.. manim:: LinearNN
:save_last_frame:
class LinearNN(Scene):
def construct(self):
edges = []
partitions = []
c = 0
layers = [2, 3, 3, 2] # the number of neurons in each layer
for i in layers:
partitions.append(list(range(c + 1, c + i + 1)))
c += i
for i, v in enumerate(layers[1:]):
last = sum(layers[:i+1])
for j in range(v):
for k in range(last - layers[i], last):
edges.append((k + 1, j + last + 1))
vertices = np.arange(1, sum(layers) + 1)
graph = Graph(
vertices,
edges,
layout='partite',
partitions=partitions,
layout_scale=3,
vertex_config={'radius': 0.20},
)
self.add(graph)
The custom tree layout can be used to show the graph
by distance from the root vertex. You must pass the root vertex
of the tree.
.. manim:: Tree
import networkx as nx
class Tree(Scene):
def construct(self):
G = nx.Graph()
G.add_node("ROOT")
for i in range(5):
G.add_node("Child_%i" % i)
G.add_node("Grandchild_%i" % i)
G.add_node("Greatgrandchild_%i" % i)
G.add_edge("ROOT", "Child_%i" % i)
G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)
self.play(Create(
Graph(list(G.nodes), list(G.edges), layout="tree", root_vertex="ROOT")))
The following code sample illustrates the use of the ``vertex_spacing``
layout parameter specific to the ``"tree"`` layout. As mentioned
above, setting ``vertex_spacing`` overrides the specified value
for ``layout_scale``, and as such it is harder to control the size
of the mobject. However, we can adjust the captured frame and
zoom out by using a :class:`.MovingCameraScene`::
class LargeTreeGeneration(MovingCameraScene):
DEPTH = 4
CHILDREN_PER_VERTEX = 3
LAYOUT_CONFIG = {"vertex_spacing": (0.5, 1)}
VERTEX_CONF = {"radius": 0.25, "color": BLUE_B, "fill_opacity": 1}
def expand_vertex(self, g, vertex_id: str, depth: int):
new_vertices = [f"{vertex_id}/{i}" for i in range(self.CHILDREN_PER_VERTEX)]
new_edges = [(vertex_id, child_id) for child_id in new_vertices]
g.add_edges(
*new_edges,
vertex_config=self.VERTEX_CONF,
positions={
k: g.vertices[vertex_id].get_center() + 0.1 * DOWN for k in new_vertices
},
)
if depth < self.DEPTH:
for child_id in new_vertices:
self.expand_vertex(g, child_id, depth + 1)
return g
def construct(self):
g = Graph(["ROOT"], [], vertex_config=self.VERTEX_CONF)
g = self.expand_vertex(g, "ROOT", 1)
self.add(g)
self.play(
g.animate.change_layout(
"tree",
root_vertex="ROOT",
layout_config=self.LAYOUT_CONFIG,
)
)
self.play(self.camera.auto_zoom(g, margin=1), run_time=0.5)
"""
def __init__(
@ -511,7 +315,7 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
) -> None:
super().__init__()
nx_graph = nx.Graph()
nx_graph = self._empty_networkx_graph()
nx_graph.add_nodes_from(vertices)
nx_graph.add_edges_from(edges)
self._graph = nx_graph
@ -564,48 +368,51 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
# build edge_config
if edge_config is None:
edge_config = {}
default_tip_config = {}
default_edge_config = {}
if edge_config:
default_tip_config = edge_config.pop("tip_config", {})
default_edge_config = {
k: v
for k, v in edge_config.items()
if k not in edges and k[::-1] not in edges
if not isinstance(
k, tuple
) # everything that is not an edge is an option
}
self._edge_config = {}
self._tip_config = {}
for e in edges:
if e in edge_config:
self._tip_config[e] = edge_config[e].pop(
"tip_config", copy(default_tip_config)
)
self._edge_config[e] = edge_config[e]
elif e[::-1] in edge_config:
self._edge_config[e] = edge_config[e[::-1]]
else:
self._tip_config[e] = copy(default_tip_config)
self._edge_config[e] = copy(default_edge_config)
self.default_edge_config = default_edge_config
self.edges = {
(u, v): edge_type(
self[u].get_center(),
self[v].get_center(),
z_index=-1,
**self._edge_config[(u, v)],
)
for (u, v) in edges
}
self._populate_edge_dict(edges, edge_type)
self.add(*self.vertices.values())
self.add(*self.edges.values())
def update_edges(graph):
for (u, v), edge in graph.edges.items():
edge.put_start_and_end_on(graph[u].get_center(), graph[v].get_center())
self.add_updater(self.update_edges)
self.add_updater(update_edges)
@staticmethod
def _empty_networkx_graph():
"""Return an empty networkx graph for the given graph type."""
raise NotImplementedError("To be implemented in concrete subclasses")
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
):
"""Helper method for populating the edges of the graph."""
raise NotImplementedError("To be implemented in concrete subclasses")
def __getitem__(self: Graph, v: Hashable) -> Mobject:
return self.vertices[v]
def __repr__(self: Graph) -> str:
return f"Graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
def _create_vertex(
self,
vertex: Hashable,
@ -903,7 +710,7 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
>>> removed = G.remove_vertices(2, 3); removed
VGroup(Line, Line, Dot, Dot)
>>> G
Graph on 1 vertices and 0 edges
Undirected graph on 1 vertices and 0 edges
"""
mobjects = []
@ -1063,9 +870,7 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
"""
if edge not in self.edges:
edge = edge[::-1]
if edge not in self.edges:
raise ValueError(f"The graph does not contain a edge '{edge}'")
raise ValueError(f"The graph does not contain a edge '{edge}'")
edge_mobject = self.edges.pop(edge)
@ -1102,15 +907,18 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
mobjects = self.remove_edges(*edges)
return AnimationGroup(*(animation(mobj, **anim_args) for mobj in mobjects))
@staticmethod
def from_networkx(nxgraph: nx.classes.graph.Graph, **kwargs) -> Graph:
"""Build a :class:`~.Graph` from a given ``networkx`` graph.
@classmethod
def from_networkx(
cls, nxgraph: nx.classes.graph.Graph | nx.classes.digraph.DiGraph, **kwargs
):
"""Build a :class:`~.Graph` or :class:`~.DiGraph` from a
given ``networkx`` graph.
Parameters
----------
nxgraph
A ``networkx`` graph.
A ``networkx`` graph or digraph.
**kwargs
Keywords to be passed to the constructor of :class:`~.Graph`.
@ -1133,7 +941,7 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
self.play(Uncreate(G))
"""
return Graph(list(nxgraph.nodes), list(nxgraph.edges), **kwargs)
return cls(list(nxgraph.nodes), list(nxgraph.edges), **kwargs)
def change_layout(
self,
@ -1174,3 +982,522 @@ class Graph(VMobject, metaclass=ConvertToOpenGL):
for v in self.vertices:
self[v].move_to(self._layout[v])
return self
class Graph(GenericGraph):
"""An undirected graph (vertices connected with edges).
The graph comes with an updater which makes the edges stick to
the vertices when moved around. See :class:`.DiGraph` for
a version with directed edges.
See also
--------
:class:`.GenericGraph`
Parameters
----------
vertices
A list of vertices. Must be hashable elements.
edges
A list of edges, specified as tuples ``(u, v)`` where both ``u``
and ``v`` are vertices. The vertex order is irrelevant.
labels
Controls whether or not vertices are labeled. If ``False`` (the default),
the vertices are not labeled; if ``True`` they are labeled using their
names (as specified in ``vertices``) via :class:`~.MathTex`. Alternatively,
custom labels can be specified by passing a dictionary whose keys are
the vertices, and whose values are the corresponding vertex labels
(rendered via, e.g., :class:`~.Text` or :class:`~.Tex`).
label_fill_color
Sets the fill color of the default labels generated when ``labels``
is set to ``True``. Has no effect for other values of ``labels``.
layout
Either one of ``"spring"`` (the default), ``"circular"``, ``"kamada_kawai"``,
``"planar"``, ``"random"``, ``"shell"``, ``"spectral"``, ``"spiral"``, ``"tree"``, and ``"partite"``
for automatic vertex positioning using ``networkx``
(see `their documentation <https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout>`_
for more details), or a dictionary specifying a coordinate (value)
for each vertex (key) for manual positioning.
layout_config
Only for automatically generated layouts. A dictionary whose entries
are passed as keyword arguments to the automatic layout algorithm
specified via ``layout`` of ``networkx``.
The ``tree`` layout also accepts a special parameter ``vertex_spacing``
passed as a keyword argument inside the ``layout_config`` dictionary.
Passing a tuple ``(space_x, space_y)`` as this argument overrides
the value of ``layout_scale`` and ensures that vertices are arranged
in a way such that the centers of siblings in the same layer are
at least ``space_x`` units apart horizontally, and neighboring layers
are spaced ``space_y`` units vertically.
layout_scale
The scale of automatically generated layouts: the vertices will
be arranged such that the coordinates are located within the
interval ``[-scale, scale]``. Some layouts accept a tuple ``(scale_x, scale_y)``
causing the first coordinate to be in the interval ``[-scale_x, scale_x]``,
and the second in ``[-scale_y, scale_y]``. Default: 2.
vertex_type
The mobject class used for displaying vertices in the scene.
vertex_config
Either a dictionary containing keyword arguments to be passed to
the class specified via ``vertex_type``, or a dictionary whose keys
are the vertices, and whose values are dictionaries containing keyword
arguments for the mobject related to the corresponding vertex.
vertex_mobjects
A dictionary whose keys are the vertices, and whose values are
mobjects to be used as vertices. Passing vertices here overrides
all other configuration options for a vertex.
edge_type
The mobject class used for displaying edges in the scene.
edge_config
Either a dictionary containing keyword arguments to be passed
to the class specified via ``edge_type``, or a dictionary whose
keys are the edges, and whose values are dictionaries containing
keyword arguments for the mobject related to the corresponding edge.
Examples
--------
First, we create a small graph and demonstrate that the edges move
together with the vertices.
.. manim:: MovingVertices
class MovingVertices(Scene):
def construct(self):
vertices = [1, 2, 3, 4]
edges = [(1, 2), (2, 3), (3, 4), (1, 3), (1, 4)]
g = Graph(vertices, edges)
self.play(Create(g))
self.wait()
self.play(g[1].animate.move_to([1, 1, 0]),
g[2].animate.move_to([-1, 1, 0]),
g[3].animate.move_to([1, -1, 0]),
g[4].animate.move_to([-1, -1, 0]))
self.wait()
There are several automatic positioning algorithms to choose from:
.. manim:: GraphAutoPosition
:save_last_frame:
class GraphAutoPosition(Scene):
def construct(self):
vertices = [1, 2, 3, 4, 5, 6, 7, 8]
edges = [(1, 7), (1, 8), (2, 3), (2, 4), (2, 5),
(2, 8), (3, 4), (6, 1), (6, 2),
(6, 3), (7, 2), (7, 4)]
autolayouts = ["spring", "circular", "kamada_kawai",
"planar", "random", "shell",
"spectral", "spiral"]
graphs = [Graph(vertices, edges, layout=lt).scale(0.5)
for lt in autolayouts]
r1 = VGroup(*graphs[:3]).arrange()
r2 = VGroup(*graphs[3:6]).arrange()
r3 = VGroup(*graphs[6:]).arrange()
self.add(VGroup(r1, r2, r3).arrange(direction=DOWN))
Vertices can also be positioned manually:
.. manim:: GraphManualPosition
:save_last_frame:
class GraphManualPosition(Scene):
def construct(self):
vertices = [1, 2, 3, 4]
edges = [(1, 2), (2, 3), (3, 4), (4, 1)]
lt = {1: [0, 0, 0], 2: [1, 1, 0], 3: [1, -1, 0], 4: [-1, 0, 0]}
G = Graph(vertices, edges, layout=lt)
self.add(G)
The vertices in graphs can be labeled, and configurations for vertices
and edges can be modified both by default and for specific vertices and
edges.
.. note::
In ``edge_config``, edges can be passed in both directions: if
``(u, v)`` is an edge in the graph, both ``(u, v)`` as well
as ``(v, u)`` can be used as keys in the dictionary.
.. manim:: LabeledModifiedGraph
:save_last_frame:
class LabeledModifiedGraph(Scene):
def construct(self):
vertices = [1, 2, 3, 4, 5, 6, 7, 8]
edges = [(1, 7), (1, 8), (2, 3), (2, 4), (2, 5),
(2, 8), (3, 4), (6, 1), (6, 2),
(6, 3), (7, 2), (7, 4)]
g = Graph(vertices, edges, layout="circular", layout_scale=3,
labels=True, vertex_config={7: {"fill_color": RED}},
edge_config={(1, 7): {"stroke_color": RED},
(2, 7): {"stroke_color": RED},
(4, 7): {"stroke_color": RED}})
self.add(g)
You can also lay out a partite graph on columns by specifying
a list of the vertices on each side and choosing the partite layout.
.. note::
All vertices in your graph which are not listed in any of the partitions
are collected in their own partition and rendered in the rightmost column.
.. manim:: PartiteGraph
:save_last_frame:
import networkx as nx
class PartiteGraph(Scene):
def construct(self):
G = nx.Graph()
G.add_nodes_from([0, 1, 2, 3])
G.add_edges_from([(0, 2), (0,3), (1, 2)])
graph = Graph(list(G.nodes), list(G.edges), layout="partite", partitions=[[0, 1]])
self.play(Create(graph))
The representation of a linear artificial neural network is facilitated
by the use of the partite layout and defining partitions for each layer.
.. manim:: LinearNN
:save_last_frame:
class LinearNN(Scene):
def construct(self):
edges = []
partitions = []
c = 0
layers = [2, 3, 3, 2] # the number of neurons in each layer
for i in layers:
partitions.append(list(range(c + 1, c + i + 1)))
c += i
for i, v in enumerate(layers[1:]):
last = sum(layers[:i+1])
for j in range(v):
for k in range(last - layers[i], last):
edges.append((k + 1, j + last + 1))
vertices = np.arange(1, sum(layers) + 1)
graph = Graph(
vertices,
edges,
layout='partite',
partitions=partitions,
layout_scale=3,
vertex_config={'radius': 0.20},
)
self.add(graph)
The custom tree layout can be used to show the graph
by distance from the root vertex. You must pass the root vertex
of the tree.
.. manim:: Tree
import networkx as nx
class Tree(Scene):
def construct(self):
G = nx.Graph()
G.add_node("ROOT")
for i in range(5):
G.add_node("Child_%i" % i)
G.add_node("Grandchild_%i" % i)
G.add_node("Greatgrandchild_%i" % i)
G.add_edge("ROOT", "Child_%i" % i)
G.add_edge("Child_%i" % i, "Grandchild_%i" % i)
G.add_edge("Grandchild_%i" % i, "Greatgrandchild_%i" % i)
self.play(Create(
Graph(list(G.nodes), list(G.edges), layout="tree", root_vertex="ROOT")))
The following code sample illustrates the use of the ``vertex_spacing``
layout parameter specific to the ``"tree"`` layout. As mentioned
above, setting ``vertex_spacing`` overrides the specified value
for ``layout_scale``, and as such it is harder to control the size
of the mobject. However, we can adjust the captured frame and
zoom out by using a :class:`.MovingCameraScene`::
class LargeTreeGeneration(MovingCameraScene):
DEPTH = 4
CHILDREN_PER_VERTEX = 3
LAYOUT_CONFIG = {"vertex_spacing": (0.5, 1)}
VERTEX_CONF = {"radius": 0.25, "color": BLUE_B, "fill_opacity": 1}
def expand_vertex(self, g, vertex_id: str, depth: int):
new_vertices = [f"{vertex_id}/{i}" for i in range(self.CHILDREN_PER_VERTEX)]
new_edges = [(vertex_id, child_id) for child_id in new_vertices]
g.add_edges(
*new_edges,
vertex_config=self.VERTEX_CONF,
positions={
k: g.vertices[vertex_id].get_center() + 0.1 * DOWN for k in new_vertices
},
)
if depth < self.DEPTH:
for child_id in new_vertices:
self.expand_vertex(g, child_id, depth + 1)
return g
def construct(self):
g = Graph(["ROOT"], [], vertex_config=self.VERTEX_CONF)
g = self.expand_vertex(g, "ROOT", 1)
self.add(g)
self.play(
g.animate.change_layout(
"tree",
root_vertex="ROOT",
layout_config=self.LAYOUT_CONFIG,
)
)
self.play(self.camera.auto_zoom(g, margin=1), run_time=0.5)
"""
@staticmethod
def _empty_networkx_graph() -> nx.Graph:
return nx.Graph()
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
):
self.edges = {
(u, v): edge_type(
self[u].get_center(),
self[v].get_center(),
z_index=-1,
**self._edge_config[(u, v)],
)
for (u, v) in edges
}
def update_edges(self, graph):
for (u, v), edge in graph.edges.items():
# Undirected graph has a Line edge
edge.put_start_and_end_on(graph[u].get_center(), graph[v].get_center())
def __repr__(self: Graph) -> str:
return f"Undirected graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
class DiGraph(GenericGraph):
"""A directed graph.
.. note::
In contrast to undirected graphs, the order in which vertices in a given
edge are specified is relevant here.
See also
--------
:class:`.GenericGraph`
Parameters
----------
vertices
A list of vertices. Must be hashable elements.
edges
A list of edges, specified as tuples ``(u, v)`` where both ``u``
and ``v`` are vertices. The edge is directed from ``u`` to ``v``.
labels
Controls whether or not vertices are labeled. If ``False`` (the default),
the vertices are not labeled; if ``True`` they are labeled using their
names (as specified in ``vertices``) via :class:`~.MathTex`. Alternatively,
custom labels can be specified by passing a dictionary whose keys are
the vertices, and whose values are the corresponding vertex labels
(rendered via, e.g., :class:`~.Text` or :class:`~.Tex`).
label_fill_color
Sets the fill color of the default labels generated when ``labels``
is set to ``True``. Has no effect for other values of ``labels``.
layout
Either one of ``"spring"`` (the default), ``"circular"``, ``"kamada_kawai"``,
``"planar"``, ``"random"``, ``"shell"``, ``"spectral"``, ``"spiral"``, ``"tree"``, and ``"partite"``
for automatic vertex positioning using ``networkx``
(see `their documentation <https://networkx.org/documentation/stable/reference/drawing.html#module-networkx.drawing.layout>`_
for more details), or a dictionary specifying a coordinate (value)
for each vertex (key) for manual positioning.
layout_config
Only for automatically generated layouts. A dictionary whose entries
are passed as keyword arguments to the automatic layout algorithm
specified via ``layout`` of ``networkx``.
The ``tree`` layout also accepts a special parameter ``vertex_spacing``
passed as a keyword argument inside the ``layout_config`` dictionary.
Passing a tuple ``(space_x, space_y)`` as this argument overrides
the value of ``layout_scale`` and ensures that vertices are arranged
in a way such that the centers of siblings in the same layer are
at least ``space_x`` units apart horizontally, and neighboring layers
are spaced ``space_y`` units vertically.
layout_scale
The scale of automatically generated layouts: the vertices will
be arranged such that the coordinates are located within the
interval ``[-scale, scale]``. Some layouts accept a tuple ``(scale_x, scale_y)``
causing the first coordinate to be in the interval ``[-scale_x, scale_x]``,
and the second in ``[-scale_y, scale_y]``. Default: 2.
vertex_type
The mobject class used for displaying vertices in the scene.
vertex_config
Either a dictionary containing keyword arguments to be passed to
the class specified via ``vertex_type``, or a dictionary whose keys
are the vertices, and whose values are dictionaries containing keyword
arguments for the mobject related to the corresponding vertex.
vertex_mobjects
A dictionary whose keys are the vertices, and whose values are
mobjects to be used as vertices. Passing vertices here overrides
all other configuration options for a vertex.
edge_type
The mobject class used for displaying edges in the scene.
edge_config
Either a dictionary containing keyword arguments to be passed
to the class specified via ``edge_type``, or a dictionary whose
keys are the edges, and whose values are dictionaries containing
keyword arguments for the mobject related to the corresponding edge.
You can further customize the tip by adding a ``tip_config`` dictionary
for global styling, or by adding the dict to a specific ``edge_config``.
Examples
--------
.. manim:: MovingDiGraph
class MovingDiGraph(Scene):
def construct(self):
vertices = [1, 2, 3, 4]
edges = [(1, 2), (2, 3), (3, 4), (1, 3), (1, 4)]
g = DiGraph(vertices, edges)
self.add(g)
self.play(
g[1].animate.move_to([1, 1, 1]),
g[2].animate.move_to([-1, 1, 2]),
g[3].animate.move_to([1, -1, -1]),
g[4].animate.move_to([-1, -1, 0]),
)
self.wait()
You can customize the edges and arrow tips globally or locally.
.. manim:: CustomDiGraph
class CustomDiGraph(Scene):
def construct(self):
vertices = [i for i in range(5)]
edges = [
(0, 1),
(1, 2),
(3, 2),
(3, 4),
]
edge_config = {
"stroke_width": 2,
"tip_config": {
"tip_shape": ArrowSquareTip,
"tip_length": 0.15,
},
(3, 4): {
"color": RED,
"tip_config": {"tip_length": 0.25, "tip_width": 0.25}
},
}
g = DiGraph(
vertices,
edges,
labels=True,
layout="circular",
edge_config=edge_config,
).scale(1.4)
self.play(Create(g))
self.wait()
Since this implementation respects the labels boundary you can also use
it for an undirected moving graph with labels.
.. manim:: UndirectedMovingDiGraph
class UndirectedMovingDiGraph(Scene):
def construct(self):
vertices = [i for i in range(5)]
edges = [
(0, 1),
(1, 2),
(3, 2),
(3, 4),
]
edge_config = {
"stroke_width": 2,
"tip_config": {"tip_length": 0, "tip_width": 0},
(3, 4): {"color": RED},
}
g = DiGraph(
vertices,
edges,
labels=True,
layout="circular",
edge_config=edge_config,
).scale(1.4)
self.play(Create(g))
self.wait()
self.play(
g[1].animate.move_to([1, 1, 1]),
g[2].animate.move_to([-1, 1, 2]),
g[3].animate.move_to([-1.5, -1.5, -1]),
g[4].animate.move_to([1, -2, -1]),
)
self.wait()
"""
@staticmethod
def _empty_networkx_graph() -> nx.DiGraph:
return nx.DiGraph()
def _populate_edge_dict(
self, edges: list[tuple[Hashable, Hashable]], edge_type: type[Mobject]
):
self.edges = {
(u, v): edge_type(
self[u],
self[v],
z_index=-1,
**self._edge_config[(u, v)],
)
for (u, v) in edges
}
for (u, v), edge in self.edges.items():
edge.add_tip(**self._tip_config[(u, v)])
def update_edges(self, graph):
"""Updates the edges to stick at their corresponding vertices.
Arrow tips need to be repositioned since otherwise they can be
deformed.
"""
for (u, v), edge in graph.edges.items():
edge_type = type(edge)
tip = edge.pop_tips()[0]
new_edge = edge_type(self[u], self[v], **self._edge_config[(u, v)])
edge.become(new_edge)
edge.add_tip(tip)
def __repr__(self: DiGraph) -> str:
return f"Directed graph on {len(self.vertices)} vertices and {len(self.edges)} edges"

View file

@ -17,7 +17,6 @@ import numbers
from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence
import numpy as np
from colour import Color
from manim import config
from manim.constants import *
@ -44,6 +43,8 @@ from manim.utils.color import (
GREEN,
WHITE,
YELLOW,
ManimColor,
ParsableManimColor,
color_gradient,
invert_color,
)
@ -431,7 +432,7 @@ class CoordinateSystem:
point: Sequence[float],
line_func: Line = DashedLine,
line_config: dict | None = None,
color: Color | None = None,
color: ParsableManimColor | None = None,
stroke_width: float = 2,
) -> Line:
"""Returns a straight line from a given axis to a point in the scene.
@ -467,7 +468,7 @@ class CoordinateSystem:
if color is None:
color = VMobject().color
line_config["color"] = color
line_config["color"] = ManimColor.parse(color)
line_config["stroke_width"] = stroke_width
axis = self.get_axis(index)
@ -715,8 +716,10 @@ class CoordinateSystem:
)
self.add(ax, a)
"""
x_scale = self.get_x_axis().scaling
y_scale = self.get_y_axis().scaling
graph = ImplicitFunction(
func=func,
func=(lambda x, y: func(x_scale.function(x), y_scale.function(y))),
x_range=self.x_range[:2],
y_range=self.y_range[:2],
min_depth=min_depth,
@ -821,7 +824,9 @@ class CoordinateSystem:
function: Callable[[float], float],
u_range: Sequence[float] | None = None,
v_range: Sequence[float] | None = None,
colorscale: Sequence[[color], float] | None = None,
colorscale: Sequence[ParsableManimColor]
| Sequence[tuple[ParsableManimColor, float]]
| None = None,
colorscale_axis: int = 2,
**kwargs,
):
@ -980,7 +985,7 @@ class CoordinateSystem:
x_val: float | None = None,
direction: Sequence[float] = RIGHT,
buff: float = MED_SMALL_BUFF,
color: Color | None = None,
color: ParsableManimColor | None = None,
dot: bool = False,
dot_config: dict | None = None,
) -> Mobject:
@ -1032,7 +1037,8 @@ class CoordinateSystem:
if dot_config is None:
dot_config = {}
color = color or graph.get_color()
if color is None:
color = graph.get_color()
label = self.x_axis._create_label_tex(label).set_color(color)
if x_val is None:
@ -1062,9 +1068,9 @@ class CoordinateSystem:
dx: float | None = 0.1,
input_sample_type: str = "left",
stroke_width: float = 1,
stroke_color: Color = BLACK,
stroke_color: ParsableManimColor = BLACK,
fill_opacity: float = 1,
color: Iterable[Color] | Color = np.array((BLUE, GREEN)),
color: Iterable[ParsableManimColor] | ParsableManimColor = (BLUE, GREEN),
show_signed_area: bool = True,
bounded_graph: ParametricFunction = None,
blend: bool = False,
@ -1163,11 +1169,12 @@ class CoordinateSystem:
rectangles = VGroup()
x_range = np.arange(*x_range)
# allows passing a string to color the graph
if type(color) is str:
colors = [color] * len(x_range)
if isinstance(color, (list, tuple)):
color = [ManimColor(c) for c in color]
else:
colors = color_gradient(color, len(x_range))
color = [ManimColor(color)]
colors = color_gradient(color, len(x_range))
for x, color in zip(x_range, colors):
if input_sample_type == "left":
@ -1222,7 +1229,7 @@ class CoordinateSystem:
self,
graph: ParametricFunction,
x_range: tuple[float, float] | None = None,
color: Color | Iterable[Color] = [BLUE, GREEN],
color: ParsableManimColor | Iterable[ParsableManimColor] = (BLUE, GREEN),
opacity: float = 0.3,
bounded_graph: ParametricFunction = None,
**kwargs,
@ -1371,7 +1378,7 @@ class CoordinateSystem:
return np.tan(self.angle_of_tangent(x, graph, **kwargs))
def plot_derivative_graph(
self, graph: ParametricFunction, color: Color = GREEN, **kwargs
self, graph: ParametricFunction, color: ParsableManimColor = GREEN, **kwargs
) -> ParametricFunction:
"""Returns the curve of the derivative of the passed graph.
@ -1479,12 +1486,12 @@ class CoordinateSystem:
x: float,
graph: ParametricFunction,
dx: float | None = None,
dx_line_color: Color = YELLOW,
dy_line_color: Color | None = None,
dx_line_color: ParsableManimColor = YELLOW,
dy_line_color: ParsableManimColor | None = None,
dx_label: float | str | None = None,
dy_label: float | str | None = None,
include_secant_line: bool = True,
secant_line_color: Color = GREEN,
secant_line_color: ParsableManimColor = GREEN,
secant_line_length: float = 10,
) -> VGroup:
"""Creates two lines representing `dx` and `df`, the labels for `dx` and `df`, and
@ -1658,11 +1665,11 @@ class CoordinateSystem:
x_val: float,
graph: ParametricFunction,
label: float | str | Mobject | None = None,
label_color: Color | None = None,
label_color: ParsableManimColor | None = None,
triangle_size: float = MED_SMALL_BUFF,
triangle_color: Color | None = WHITE,
triangle_color: ParsableManimColor | None = WHITE,
line_func: Line = Line,
line_color: Color = YELLOW,
line_color: ParsableManimColor = YELLOW,
) -> VGroup:
"""Creates a labelled triangle marker with a vertical line from the x-axis
to a curve at a given x-value.
@ -1771,6 +1778,17 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
# x_min must be > 0 because log is undefined at 0.
graph = ax.plot(lambda x: x ** 2, x_range=[0.001, 10], use_smoothing=False)
self.add(ax, graph)
Styling arguments can be passed to the underlying :class:`.NumberLine`
mobjects that represent the axes:
.. manim:: AxesWithDifferentTips
:save_last_frame:
class AxesWithDifferentTips(Scene):
def construct(self):
ax = Axes(axis_config={'tip_shape': StealthTip})
self.add(ax)
"""
def __init__(
@ -1972,40 +1990,47 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
self.add(plane, dot_scene, ax, dot_axes, lines)
"""
coords = np.asarray(coords)
origin = self.x_axis.number_to_point(
self._origin_shift([self.x_axis.x_min, self.x_axis.x_max]),
)
coords = np.asarray(coords)
# Is coords in the format ([[x1 y1 z1] [x2 y2 z2] ...])? (True)
# Or is coords in the format (x, y, z) or ([x1 x2 ...], [y1 y2 ...], [z1 z2 ...])? (False)
# The latter is preferred.
are_coordinates_transposed = False
# if called like coords_to_point(1, 2, 3), then coords is a 1x3 array
transposed = False
if coords.ndim == 1:
# original implementation of coords_to_point for performance in the legacy case
result = np.array(origin)
for axis, number in zip(self.get_axes(), coords):
result += axis.number_to_point(number) - origin
return result
# if called like coords_to_point([1, 2, 3],[4, 5, 6]), then it shall be used as [1,4], [2,5], [3,6] and return the points as ([x_0,x_1],[y_0,y_1],[z_0,z_1])
elif coords.ndim == 2:
coords = coords.T
transposed = True
# if called like coords_to_point(np.array([[1, 2, 3],[4,5,6]])), reduce dimension by 1
elif coords.ndim == 3:
coords = np.squeeze(coords)
# else the coords is a Nx1, Nx2, Nx3 array so we do not need to modify the array
# If coords is in the format ([[x1 y1 z1] [x2 y2 z2] ...]):
if coords.ndim == 3:
# Extract from original tuple: now coords looks like [[x y z]] or [[x1 y1 z1] [x2 y2 z2] ...].
coords = coords[0]
# If there's a single coord (coords = [[x y z]]), extract it so that
# coords = [x y z] and coords_to_point returns a single point.
if coords.shape[0] == 1:
coords = coords[0]
# Else, if coords looks more like [[x1 y1 z1] [x2 y2 z2] ...], transform them (by
# transposing) into the format [[x1 x2 ...] [y1 y2 ...] [z1 z2 ...]] for later processing.
else:
coords = coords.T
are_coordinates_transposed = True
# Otherwise, coords already looked like (x, y, z) or ([x1 x2 ...], [y1 y2 ...], [z1 z2 ...]),
# so no further processing is needed.
points = origin + np.sum(
[
axis.number_to_point(number) - origin
for number, axis in zip(coords.T, self.get_axes())
],
axis=0,
)
# if called with single coord, then return a point instead of a list of points
if transposed:
return points.T
return points
# Now coords should either look like [x y z] or [[x1 x2 ...] [y1 y2 ...] [z1 z2 ...]],
# so it can be iterated directly. Each element is either a float representing a single
# coordinate, or a float ndarray of coordinates corresponding to a single axis.
# Although "points" and "nums" are in plural, there might be a single point or number.
points = self.x_axis.number_to_point(coords[0])
other_axes = self.axes.submobjects[1:]
for axis, nums in zip(other_axes, coords[1:]):
points += axis.number_to_point(nums) - origin
# Return points as is, except if coords originally looked like
# ([x1 x2 ...], [y1 y2 ...], [z1 z2 ...]), which is determined by the conditions below. In
# that case, the current implementation requires that the results have to be transposed.
if are_coordinates_transposed or points.ndim == 1:
return points
return points.T
def point_to_coords(self, point: Sequence[float]) -> np.ndarray:
"""Accepts a point from the scene and returns its coordinates with respect to the axes.
@ -2075,10 +2100,11 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
x_label: float | str | Mobject = "x",
y_label: float | str | Mobject = "y",
) -> VGroup:
"""Defines labels for the x_axis and y_axis of the graph.
"""Defines labels for the x-axis and y-axis of the graph.
For increased control over the position of the labels,
use :meth:`get_x_axis_label` and :meth:`get_y_axis_label`.
use :meth:`~.CoordinateSystem.get_x_axis_label` and
:meth:`~.CoordinateSystem.get_y_axis_label`.
Parameters
----------
@ -2094,8 +2120,8 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
.. seealso::
:meth:`.get_x_axis_label`
:meth:`.get_y_axis_label`
:meth:`~.CoordinateSystem.get_x_axis_label`
:meth:`~.CoordinateSystem.get_y_axis_label`
Examples
--------
@ -2122,7 +2148,7 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
x_values: Iterable[float],
y_values: Iterable[float],
z_values: Iterable[float] | None = None,
line_color: Color = YELLOW,
line_color: ParsableManimColor = YELLOW,
add_vertex_dots: bool = True,
vertex_dot_radius: float = DEFAULT_DOT_RADIUS,
vertex_dot_style: dict | None = None,
@ -2277,7 +2303,6 @@ class ThreeDAxes(Axes):
gloss=0.5,
**kwargs,
):
super().__init__(
x_range=x_range,
x_length=x_length,
@ -2466,7 +2491,9 @@ class ThreeDAxes(Axes):
"""Defines labels for the x_axis and y_axis of the graph.
For increased control over the position of the labels,
use :meth:`.get_x_axis_label` and :meth:`.get_y_axis_label`.
use :meth:`~.CoordinateSystem.get_x_axis_label`,
:meth:`~.ThreeDAxes.get_y_axis_label`, and
:meth:`~.ThreeDAxes.get_z_axis_label`.
Parameters
----------
@ -2484,9 +2511,9 @@ class ThreeDAxes(Axes):
.. seealso::
:meth:`.get_x_axis_label`
:meth:`.get_y_axis_label`
:meth:`.get_z_axis_label`
:meth:`~.CoordinateSystem.get_x_axis_label`
:meth:`~.ThreeDAxes.get_y_axis_label`
:meth:`~.ThreeDAxes.get_z_axis_label`
Examples
--------
@ -2600,7 +2627,6 @@ class NumberPlane(Axes):
make_smooth_after_applying_functions: bool = True,
**kwargs,
):
# configs
self.axis_config = {
"stroke_width": 2,

View file

@ -128,7 +128,6 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
return self.function(t)
def generate_points(self):
if self.discontinuities is not None:
discontinuities = filter(
lambda t: self.t_min <= t <= self.t_max,
@ -159,7 +158,7 @@ class ParametricFunction(VMobject, metaclass=ConvertToOpenGL):
x, y, z = self.function(t_range)
if not isinstance(z, np.ndarray):
z = np.zeros_like(x)
points = np.stack(zip(x, y, z), axis=0)
points = np.stack([x, y, z], axis=1)
else:
points = np.array([self.function(t) for t in t_range])
@ -203,7 +202,6 @@ class FunctionGraph(ParametricFunction):
"""
def __init__(self, function, x_range=None, color=YELLOW, **kwargs):
if x_range is None:
x_range = np.array([-config["frame_x_radius"], config["frame_x_radius"]])

View file

@ -2,9 +2,15 @@
from __future__ import annotations
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
__all__ = ["NumberLine", "UnitInterval"]
from typing import Iterable, Sequence
from typing import TYPE_CHECKING, Callable, Iterable, Sequence
if TYPE_CHECKING:
from manim.mobject.geometry.tips import ArrowTip
import numpy as np
@ -49,6 +55,10 @@ class NumberLine(Line):
The width of the tip.
tip_height
The height of the tip.
tip_shape
The mobject class used to construct the tip, or ``None`` (the
default) for the default arrow tip. Passed classes have to inherit
from :class:`.ArrowTip`.
include_numbers
Whether to add numbers to the tick marks. The number of decimal places is determined
by the step size, this default can be overridden by ``decimal_number_config``.
@ -140,6 +150,7 @@ class NumberLine(Line):
include_tip: bool = False,
tip_width: float = DEFAULT_ARROW_TIP_LENGTH,
tip_height: float = DEFAULT_ARROW_TIP_LENGTH,
tip_shape: type[ArrowTip] | None = None,
# numbers/labels
include_numbers: bool = False,
font_size: float = 36,
@ -217,7 +228,11 @@ class NumberLine(Line):
self.center()
if self.include_tip:
self.add_tip(tip_length=self.tip_height, tip_width=self.tip_width)
self.add_tip(
tip_length=self.tip_height,
tip_width=self.tip_width,
tip_shape=tip_shape,
)
self.tip.set_stroke(self.stroke_color, self.stroke_width)
if self.include_ticks:
@ -562,20 +577,18 @@ class NumberLine(Line):
direction = self.label_direction if direction is None else direction
buff = self.line_to_number_buff if buff is None else buff
font_size = self.font_size if font_size is None else font_size
label_constructor = (
self.label_constructor if label_constructor is None else label_constructor
)
if label_constructor is None:
label_constructor = self.label_constructor
labels = VGroup()
for x, label in dict_values.items():
# TODO: remove this check and ability to call
# this method via CoordinateSystem.add_coordinates()
# must be explicitly called
if isinstance(label, str) and self.label_constructor is MathTex:
if isinstance(label, str) and label_constructor is MathTex:
label = Tex(label)
else:
label = self._create_label_tex(label)
label = self._create_label_tex(label, label_constructor)
if hasattr(label, "font_size"):
label.font_size = font_size
@ -589,26 +602,36 @@ class NumberLine(Line):
return self
def _create_label_tex(
self, label_tex: str | float | VMobject, **kwargs
self,
label_tex: str | float | VMobject,
label_constructor: Callable | None = None,
**kwargs,
) -> VMobject:
"""Checks if the label is a :class:`~.VMobject`, otherwise, creates a
label according to :attr:`label_constructor`.
label by passing ``label_tex`` to ``label_constructor``.
Parameters
----------
label_tex
The label to be compared against the above types.
The label for which a mobject should be created. If the label already
is a mobject, no new mobject is created.
label_constructor
Optional. A class or function returning a mobject when
passing ``label_tex`` as an argument. If ``None`` is passed
(the default), the label constructor from the :attr:`.label_constructor`
attribute is used.
Returns
-------
:class:`~.VMobject`
The label.
"""
if isinstance(label_tex, VMobject):
if label_constructor is None:
label_constructor = self.label_constructor
if isinstance(label_tex, (VMobject, OpenGLVMobject)):
return label_tex
else:
return self.label_constructor(label_tex, **kwargs)
return label_constructor(label_tex, **kwargs)
@staticmethod
def _decimal_places_from_step(step) -> int:

View file

@ -8,7 +8,6 @@ __all__ = ["SampleSpace", "BarChart"]
from typing import Iterable, MutableSequence, Sequence
import numpy as np
from colour import Color
from manim import config, logger
from manim.constants import *
@ -26,6 +25,7 @@ from manim.utils.color import (
LIGHT_GREY,
MAROON_B,
YELLOW,
ParsableManimColor,
color_gradient,
)
from manim.utils.iterables import tuplify
@ -34,7 +34,8 @@ EPSILON = 0.0001
class SampleSpace(Rectangle):
"""
"""A mobject representing a twodimensional rectangular
sampling space.
Examples
--------
@ -254,7 +255,6 @@ class BarChart(Axes):
bar_stroke_width: float = 3,
**kwargs,
):
if isinstance(bar_colors, str):
logger.warning(
"Passing a string to `bar_colors` has been deprecated since v0.15.2 and will be removed after v0.17.0, the parameter must be a list. "
@ -401,7 +401,7 @@ class BarChart(Axes):
def get_bar_labels(
self,
color: Color | None = None,
color: ParsableManimColor | None = None,
font_size: float = 24,
buff: float = MED_SMALL_BUFF,
label_constructor: type[VMobject] = Tex,
@ -498,7 +498,6 @@ class BarChart(Axes):
if chart_val != 0:
quotient = value / chart_val
if quotient < 0:
aligned_edge = UP if chart_val > 0 else DOWN
# if the bar is already positive, then we now want to move it

View file

@ -14,15 +14,15 @@ if TYPE_CHECKING:
class _ScaleBase:
"""Scale baseclass for graphing/functions."""
"""Scale baseclass for graphing/functions.
Parameters
----------
custom_labels
Whether to create custom labels when plotted on a :class:`~.NumberLine`.
"""
def __init__(self, custom_labels: bool = False):
"""
Parameters
----------
custom_labels
Whether to create custom labels when plotted on a :class:`~.NumberLine`.
"""
self.custom_labels = custom_labels
def function(self, value: float) -> float:

View file

@ -4,19 +4,100 @@ from __future__ import annotations
__all__ = ["ManimBanner"]
import svgelements as se
from manim.animation.updaters.update import UpdateFromAlphaFunc
from manim.mobject.geometry.arc import Circle
from manim.mobject.geometry.polygram import Square, Triangle
from manim.mobject.text.tex_mobject import MathTex, Tex
from .. import constants as cst
from ..animation.animation import override_animation
from ..animation.composition import AnimationGroup, Succession
from ..animation.creation import Create, SpiralIn
from ..animation.fading import FadeIn
from ..constants import DOWN, LEFT, ORIGIN, RIGHT, TAU, UP
from ..mobject.svg.svg_mobject import VMobjectFromSVGPath
from ..mobject.types.vectorized_mobject import VGroup
from ..utils.rate_functions import ease_in_out_cubic, ease_out_sine, smooth
from ..utils.tex_templates import TexFontTemplates
from ..utils.rate_functions import ease_in_out_cubic, smooth
MANIM_SVG_PATHS: list[se.Path] = [
se.Path( # double stroke letter M
"M4.64259-2.092154L2.739726-6.625156C2.660025-6.824408 2.650062-6.824408 "
"2.381071-6.824408H.52802C.348692-6.824408 .199253-6.824408 .199253-6.645"
"081C.199253-6.475716 .37858-6.475716 .428394-6.475716C.547945-6.475716 ."
"816936-6.455791 1.036115-6.37609V-1.05604C1.036115-.846824 1.036115-.408"
"468 .358655-.348692C.169365-.328767 .169365-.18929 .169365-.179328C.1693"
"65 0 .328767 0 .508095 0H2.052304C2.231631 0 2.381071 0 2.381071-.179328"
"C2.381071-.268991 2.30137-.33873 2.221669-.348692C1.454545-.408468 1.454"
"545-.826899 1.454545-1.05604V-6.017435L1.464508-6.027397L3.895392-.20921"
"5C3.975093-.029888 4.044832 0 4.104608 0C4.224159 0 4.254047-.079701 4.3"
"03861-.199253L6.744707-6.027397L6.75467-6.017435V-1.05604C6.75467-.84682"
"4 6.75467-.408468 6.07721-.348692C5.88792-.328767 5.88792-.18929 5.88792"
"-.179328C5.88792 0 6.047323 0 6.22665 0H8.886675C9.066002 0 9.215442 0 9"
".215442-.179328C9.215442-.268991 9.135741-.33873 9.05604-.348692C8.28891"
"7-.408468 8.288917-.826899 8.288917-1.05604V-5.768369C8.288917-5.977584 "
"8.288917-6.41594 8.966376-6.475716C9.066002-6.485679 9.155666-6.535492 9"
".155666-6.645081C9.155666-6.824408 9.006227-6.824408 8.826899-6.824408H6"
".90411C6.645081-6.824408 6.625156-6.824408 6.535492-6.615193L4.64259-2.0"
"92154ZM4.343711-1.912827C4.423412-1.743462 4.433375-1.733499 4.552927-1."
"693649L4.11457-.637609H4.094645L1.823163-6.057285C1.77335-6.1868 1.69364"
"9-6.356164 1.554172-6.475716H2.420922L4.343711-1.912827ZM1.334994-.34869"
"2H1.165629C1.185554-.37858 1.205479-.408468 1.225405-.428394C1.235367-.4"
"38356 1.235367-.448319 1.24533-.458281L1.334994-.348692ZM7.103362-6.4757"
"16H8.159402C7.940224-6.22665 7.940224-5.967621 7.940224-5.788294V-1.0361"
"15C7.940224-.856787 7.940224-.597758 8.169365-.348692H6.884184C7.103362-"
".597758 7.103362-.856787 7.103362-1.036115V-6.475716Z"
),
se.Path( # letter a
"M1.464508-4.024907C1.464508-4.234122 1.743462-4.393524 2.092154-4.393524"
"C2.669988-4.393524 2.929016-4.124533 2.929016-3.516812V-2.789539C1.77335"
"-2.440847 .249066-2.042341 .249066-.916563C.249066-.308842 .71731 .13947"
"7 1.354919 .139477C1.92279 .139477 2.381071-.059776 2.929016-.557908C3.0"
"38605-.049813 3.257783 .139477 3.745953 .139477C4.174346 .139477 4.48318"
"8-.019925 4.861768-.428394L4.712329-.637609L4.612702-.537983C4.582814-.5"
"08095 4.552927-.498132 4.503113-.498132C4.363636-.498132 4.293898-.58779"
"6 4.293898-.747198V-3.347447C4.293898-4.184309 3.536737-4.712329 2.32129"
"5-4.712329C1.195517-4.712329 .438356-4.204234 .438356-3.457036C.438356-3"
".048568 .67746-2.799502 1.085928-2.799502C1.484433-2.799502 1.763387-3.0"
"38605 1.763387-3.377335C1.763387-3.676214 1.464508-3.88543 1.464508-4.02"
"4907ZM2.919054-.996264C2.650062-.687422 2.450809-.56787 2.211706-.56787C"
"1.912827-.56787 1.703611-.836862 1.703611-1.235367C1.703611-1.8132 2.122"
"042-2.231631 2.919054-2.440847V-.996264Z"
),
se.Path( # letter n
"M2.948941-4.044832C3.297634-4.044832 3.466999-3.775841 3.466999-3.217933"
"V-.806974C3.466999-.438356 3.337484-.278954 2.998755-.239103V0H5.339975V"
"-.239103C4.951432-.268991 4.851806-.388543 4.851806-.806974V-3.307597C4."
"851806-4.164384 4.323786-4.712329 3.506849-4.712329C2.909091-4.712329 2."
"450809-4.433375 2.082192-3.845579V-4.592777H.179328V-4.353674C.617684-4."
"283935 .707347-4.184309 .707347-3.765878V-.836862C.707347-.418431 .62764"
"6-.328767 .179328-.239103V0H2.580324V-.239103C2.211706-.288917 2.092154-"
".438356 2.092154-.806974V-3.466999C2.092154-3.576588 2.530511-4.044832 2"
".948941-4.044832Z"
),
se.Path( # letter i
"M2.15193-4.592777H.239103V-4.353674C.67746-4.26401 .767123-4.174346 .767"
"123-3.765878V-.836862C.767123-.428394 .697385-.348692 .239103-.239103V0H"
"2.6401V-.239103C2.291407-.288917 2.15193-.428394 2.15193-.806974V-4.5927"
"77ZM1.454545-6.884184C1.026152-6.884184 .67746-6.535492 .67746-6.117061C"
".67746-5.668742 1.006227-5.339975 1.444583-5.339975S2.221669-5.668742 2."
"221669-6.107098C2.221669-6.535492 1.882939-6.884184 1.454545-6.884184Z"
),
se.Path( # letter m
"M2.929016-4.044832C3.317559-4.044832 3.466999-3.815691 3.466999-3.217933"
"V-.806974C3.466999-.398506 3.35741-.268991 2.988792-.239103V0H5.32005V-."
"239103C4.971357-.278954 4.851806-.428394 4.851806-.806974V-3.466999C4.85"
"1806-3.576588 5.310087-4.044832 5.69863-4.044832C6.07721-4.044832 6.2266"
"5-3.805729 6.22665-3.217933V-.806974C6.22665-.388543 6.117061-.268991 5."
"738481-.239103V0H8.109589V-.239103C7.721046-.259029 7.611457-.37858 7.61"
"1457-.806974V-3.307597C7.611457-4.164384 7.083437-4.712329 6.266501-4.71"
"2329C5.69863-4.712329 5.32005-4.483188 4.801993-3.845579C4.503113-4.4732"
"25 4.154421-4.712329 3.526775-4.712329S2.440847-4.443337 2.062267-3.8455"
"79V-4.592777H.179328V-4.353674C.617684-4.293898 .707347-4.174346 .707347"
"-3.765878V-.836862C.707347-.428394 .617684-.318804 .179328-.239103V0H2.5"
"50436V-.239103C2.201743-.288917 2.092154-.428394 2.092154-.806974V-3.466"
"999C2.092154-3.58655 2.530511-4.044832 2.929016-4.044832Z"
),
]
class ManimBanner(VGroup):
@ -67,27 +148,32 @@ class ManimBanner(VGroup):
self.font_color = "#ece6e2" if dark_theme else "#343434"
self.scale_factor = 1
self.M = MathTex(r"\mathbb{M}").scale(7).set_color(self.font_color)
self.M.shift(2.25 * LEFT + 1.5 * UP)
self.M = VMobjectFromSVGPath(MANIM_SVG_PATHS[0]).flip(cst.RIGHT).center()
self.M.set(stroke_width=0).scale(
7 * cst.DEFAULT_FONT_SIZE * cst.SCALE_FACTOR_PER_FONT_POINT
)
self.M.set_fill(color=self.font_color, opacity=1).shift(
2.25 * cst.LEFT + 1.5 * cst.UP
)
self.circle = Circle(color=logo_green, fill_opacity=1).shift(LEFT)
self.square = Square(color=logo_blue, fill_opacity=1).shift(UP)
self.triangle = Triangle(color=logo_red, fill_opacity=1).shift(RIGHT)
self.circle = Circle(color=logo_green, fill_opacity=1).shift(cst.LEFT)
self.square = Square(color=logo_blue, fill_opacity=1).shift(cst.UP)
self.triangle = Triangle(color=logo_red, fill_opacity=1).shift(cst.RIGHT)
self.shapes = VGroup(self.triangle, self.square, self.circle)
self.add(self.shapes, self.M)
self.move_to(ORIGIN)
self.move_to(cst.ORIGIN)
anim = VGroup()
for i, ch in enumerate("anim"):
tex = Tex(
"\\textbf{" + ch + "}",
tex_template=TexFontTemplates.gnu_freeserif_freesans,
for ind, path in enumerate(MANIM_SVG_PATHS[1:]):
tex = VMobjectFromSVGPath(path).flip(cst.RIGHT).center()
tex.set(stroke_width=0).scale(
cst.DEFAULT_FONT_SIZE * cst.SCALE_FACTOR_PER_FONT_POINT
)
if i != 0:
if ind > 0:
tex.next_to(anim, buff=0.01)
tex.align_to(self.M, DOWN)
tex.align_to(self.M, cst.DOWN)
anim.add(tex)
anim.set_color(self.font_color)
anim.set_fill(color=self.font_color, opacity=1)
anim.height = m_height_over_anim_height * self.M.height
# Note: "anim" is only shown in the expanded state
@ -181,7 +267,7 @@ class ManimBanner(VGroup):
m_shape_offset = 6.25 * self.scale_factor
shape_sliding_overshoot = self.scale_factor * 0.8
m_anim_buff = 0.06
self.anim.next_to(self.M, buff=m_anim_buff).align_to(self.M, DOWN)
self.anim.next_to(self.M, buff=m_anim_buff).align_to(self.M, cst.DOWN)
self.anim.set_opacity(0)
self.shapes.save_state()
m_clone = self.anim[-1].copy()
@ -193,7 +279,7 @@ class ManimBanner(VGroup):
def shift(vector):
self.shapes.restore()
left_group.align_to(self.M.saved_state, LEFT)
left_group.align_to(self.M.saved_state, cst.LEFT)
if direction == "right":
self.shapes.shift(vector)
elif direction == "center":
@ -203,13 +289,13 @@ class ManimBanner(VGroup):
left_group.shift(-vector)
def slide_and_uncover(mob, alpha):
shift(alpha * (m_shape_offset + shape_sliding_overshoot) * RIGHT)
shift(alpha * (m_shape_offset + shape_sliding_overshoot) * cst.RIGHT)
# Add letters when they are covered
for letter in mob.anim:
if mob.square.get_center()[0] > letter.get_center()[0]:
letter.set_opacity(1)
self.add(letter)
self.add_to_back(letter)
# Finish animation
if alpha == 1:
@ -225,7 +311,7 @@ class ManimBanner(VGroup):
m_clone.move_to(mob.anim[-1])
mob.anim.set_opacity(1)
shift(alpha * shape_sliding_overshoot * LEFT)
shift(alpha * shape_sliding_overshoot * cst.LEFT)
if alpha == 1:
mob.remove(m_clone)

View file

@ -74,6 +74,39 @@ def matrix_to_mobject(matrix):
class Matrix(VMobject, metaclass=ConvertToOpenGL):
"""A mobject that displays a matrix on the screen.
Parameters
----------
matrix
A numpy 2d array or list of lists.
v_buff
Vertical distance between elements, by default 0.8.
h_buff
Horizontal distance between elements, by default 1.3.
bracket_h_buff
Distance of the brackets from the matrix, by default ``MED_SMALL_BUFF``.
bracket_v_buff
Height of the brackets, by default ``MED_SMALL_BUFF``.
add_background_rectangles_to_entries
``True`` if should add backgraound rectangles to entries, by default ``False``.
include_background_rectangle
``True`` if should include background rectangle, by default ``False``.
element_to_mobject
The mobject class used to construct the elements, by default :class:`~.MathTex`.
element_to_mobject_config
Additional arguments to be passed to the constructor in ``element_to_mobject``,
by default ``{}``.
element_alignment_corner
The corner to which elements are aligned, by default ``DR``.
left_bracket
The left bracket type, by default ``"["``.
right_bracket
The right bracket type, by default ``"]"``.
stretch_brackets
``True`` if should stretch the brackets to fit the height of matrix contents, by default ``True``.
bracket_config
Additional arguments to be passed to :class:`~.MathTex` when constructing
the brackets.
Examples
--------
The first example shows a variety of uses of this module while the second example
@ -146,43 +179,6 @@ class Matrix(VMobject, metaclass=ConvertToOpenGL):
bracket_config: dict = {},
**kwargs,
):
"""
Parameters
----------
matrix
A numpy 2d array or list of lists.
v_buff
Vertical distance between elements, by default 0.8.
h_buff
Horizontal distance between elements, by default 1.3.
bracket_h_buff
Distance of the brackets from the matrix, by default ``MED_SMALL_BUFF``.
bracket_v_buff
Height of the brackets, by default ``MED_SMALL_BUFF``.
add_background_rectangles_to_entries
``True`` if should add backgraound rectangles to entries, by default ``False``.
include_background_rectangle
``True`` if should include background rectangle, by default ``False``.
element_to_mobject
The mobject class used to construct the elements, by default :class:`~.MathTex`.
element_to_mobject_config
Additional arguments to be passed to the constructor in ``element_to_mobject``,
by default ``{}``.
element_alignment_corner
The corner to which elements are aligned, by default ``DR``.
left_bracket
The left bracket type, by default ``"["``.
right_bracket
The right bracket type, by default ``"]"``.
stretch_brackets
``True`` if should stretch the brackets to fit the height of matrix contents, by default ``True``.
bracket_config
Additional arguments to be passed to :class:`~.MathTex` when constructing
the brackets.
"""
self.v_buff = v_buff
self.h_buff = h_buff
self.bracket_h_buff = bracket_h_buff

View file

@ -1,4 +1,5 @@
"""Base classes for objects that can be displayed."""
from __future__ import annotations
__all__ = ["Mobject", "Group", "override_animate"]
@ -29,7 +30,6 @@ from typing import (
)
import numpy as np
from colour import Color
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
@ -39,7 +39,8 @@ from ..utils.color import (
BLACK,
WHITE,
YELLOW_C,
Colors,
ManimColor,
ParsableManimColor,
color_gradient,
interpolate_color,
)
@ -91,7 +92,14 @@ class Mobject:
cls._add_intrinsic_animation_overrides()
cls._original__init__ = cls.__init__
def __init__(self, color=WHITE, name=None, dim=3, target=None, z_index=0):
def __init__(
self,
color: ParsableManimColor | list[ParsableManimColor] = WHITE,
name=None,
dim=3,
target=None,
z_index=0,
):
self.name = self.__class__.__name__ if name is None else name
self.dim = dim
self.target = target
@ -100,7 +108,7 @@ class Mobject:
self.submobjects = []
self.updaters = []
self.updating_suspended = False
self.color = Color(color) if color else None
self.color: ManimColor = ManimColor.parse(color)
self.reset_points()
self.generate_points()
@ -160,7 +168,7 @@ class Mobject:
The animation type to be overridden
override_func
The function returning an animation replacing the default animation. It gets
passed the parameters given to the animnation constructor.
passed the parameters given to the animation constructor.
Raises
------
@ -201,10 +209,10 @@ class Mobject:
>>> from manim import Square, GREEN
>>> Square.set_default(color=GREEN, fill_opacity=0.25)
>>> s = Square(); s.color, s.fill_opacity
(<Color #83c167>, 0.25)
(ManimColor('#83C167'), 0.25)
>>> Square.set_default()
>>> s = Square(); s.color, s.fill_opacity
(<Color white>, 0.0)
(ManimColor('#FFFFFF'), 0.0)
.. manim:: ChangedDefaultTextcolor
:save_last_frame:
@ -971,7 +979,11 @@ class Mobject:
else:
self.updaters.insert(index, update_function)
if call_updater:
update_function(self, 0)
parameters = get_parameters(update_function)
if "dt" in parameters:
update_function(self, 0)
else:
update_function(self)
return self
def remove_updater(self, update_function: Updater):
@ -1369,6 +1381,13 @@ class Mobject:
# Positioning methods
def center(self):
"""Moves the center of the mobject to the center of the scene.
Returns
-------
:class:`.Mobject`
The centered mobject.
"""
self.shift(-self.get_center())
return self
@ -1683,7 +1702,7 @@ class Mobject:
# Background rectangle
def add_background_rectangle(
self, color: Colors | None = None, opacity: float = 0.75, **kwargs
self, color: ParsableManimColor | None = None, opacity: float = 0.75, **kwargs
):
"""Add a BackgroundRectangle as submobject.
@ -1735,7 +1754,9 @@ class Mobject:
# Color functions
def set_color(self, color: Color = YELLOW_C, family: bool = True):
def set_color(
self, color: ParsableManimColor = YELLOW_C, family: bool = True
) -> Mobject:
"""Condition is function which takes in one arguments, (x, y, z).
Here it just recurses to submobjects, but in subclasses this
should be further implemented based on the the inner workings
@ -1744,19 +1765,30 @@ class Mobject:
if family:
for submob in self.submobjects:
submob.set_color(color, family=family)
self.color = Color(color)
self.color = ManimColor.parse(color)
return self
def set_color_by_gradient(self, *colors):
def set_color_by_gradient(self, *colors: Iterable[ParsableManimColor]):
"""Set the color of this mobject's submobjects along the specified
gradient.
Parameters
----------
colors
The colors to use for the gradient. Use like
``set_color_by_gradient(RED, BLUE, GREEN)``.
"""
self.set_submobject_colors_by_gradient(*colors)
return self
def set_colors_by_radial_gradient(
self,
center=None,
radius=1,
inner_color=WHITE,
outer_color=BLACK,
radius: float = 1,
inner_color: ParsableManimColor = WHITE,
outer_color: ParsableManimColor = BLACK,
):
self.set_submobject_colors_by_radial_gradient(
center,
@ -1766,7 +1798,7 @@ class Mobject:
)
return self
def set_submobject_colors_by_gradient(self, *colors):
def set_submobject_colors_by_gradient(self, *colors: Iterable[ParsableManimColor]):
if len(colors) == 0:
raise ValueError("Need at least one color")
elif len(colors) == 1:
@ -1782,9 +1814,9 @@ class Mobject:
def set_submobject_colors_by_radial_gradient(
self,
center=None,
radius=1,
inner_color=WHITE,
outer_color=BLACK,
radius: float = 1,
inner_color: ParsableManimColor = WHITE,
outer_color: ParsableManimColor = BLACK,
):
if center is None:
center = self.get_center()
@ -1801,7 +1833,7 @@ class Mobject:
self.set_color(self.color)
return self
def fade_to(self, color, alpha, family=True):
def fade_to(self, color: ParsableManimColor, alpha: float, family: bool = True):
if self.get_num_points() > 0:
new_color = interpolate_color(self.get_color(), color, alpha)
self.set_color(new_color, family=False)
@ -1810,13 +1842,13 @@ class Mobject:
submob.fade_to(color, alpha)
return self
def fade(self, darkness=0.5, family=True):
def fade(self, darkness: float = 0.5, family: bool = True):
if family:
for submob in self.submobjects:
submob.fade(darkness, family)
return self
def get_color(self):
def get_color(self) -> ManimColor:
"""Returns the color of the :class:`~.Mobject`"""
return self.color
@ -1838,16 +1870,29 @@ class Mobject:
self.become(self.saved_state)
return self
##
def reduce_across_dimension(self, points_func, reduce_func, dim):
points = self.get_all_points()
if points is None or len(points) == 0:
# Note, this default means things like empty VGroups
# will appear to have a center at [0, 0, 0]
def reduce_across_dimension(self, reduce_func, dim: int) -> float:
"""Find the min or max value from a dimension across all points in this and submobjects."""
assert dim >= 0 and dim <= 2
if len(self.submobjects) == 0 and len(self.points) == 0:
# If we have no points and no submobjects, return 0 (e.g. center)
return 0
values = points_func(points[:, dim])
return reduce_func(values)
# If we do not have points (but do have submobjects)
# use only the points from those.
if len(self.points) == 0:
rv = None
else:
# Otherwise, be sure to include our own points
rv = reduce_func(self.points[:, dim])
# Recursively ask submobjects (if any) for the biggest/
# smallest dimension they have and compare it to the return value.
for mobj in self.submobjects:
value = mobj.reduce_across_dimension(reduce_func, dim)
if rv is None:
rv = value
else:
rv = reduce_func([value, rv])
return rv
def nonempty_submobjects(self):
return [
@ -1856,13 +1901,23 @@ class Mobject:
if len(submob.submobjects) != 0 or len(submob.points) != 0
]
def get_merged_array(self, array_attr):
def get_merged_array(self, array_attr) -> np.ndarray:
"""Return all of a given attribute from this mobject and all submobjects.
May contain duplicates; the order is in a depth-first (pre-order)
traversal of the submobjects.
"""
result = getattr(self, array_attr)
for submob in self.submobjects:
result = np.append(result, submob.get_merged_array(array_attr), axis=0)
return result
def get_all_points(self):
def get_all_points(self) -> np.ndarray:
"""Return all points from this mobject and all submobjects.
May contain duplicates; the order is in a depth-first (pre-order)
traversal of the submobjects.
"""
return self.get_merged_array("points")
# Getters
@ -1983,10 +2038,9 @@ class Mobject:
def length_over_dim(self, dim):
"""Measure the length of an :class:`~.Mobject` in a certain direction."""
return self.reduce_across_dimension(
np.max,
np.max,
max,
dim,
) - self.reduce_across_dimension(np.min, np.min, dim)
) - self.reduce_across_dimension(min, dim)
def get_coord(self, dim, direction=ORIGIN):
"""Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
@ -2094,17 +2148,12 @@ class Mobject:
self,
mobject_or_point: Mobject | np.ndarray | list,
direction=ORIGIN,
alignment_vect=UP,
):
"""Aligns mobject to another :class:`~.Mobject` in a certain direction.
Examples:
mob1.align_to(mob2, UP) moves mob1 vertically so that its
top edge lines ups with mob2's top edge.
mob1.align_to(mob2, alignment_vect = RIGHT) moves mob1
horizontally so that it's center is directly above/below
the center of mob2
"""
if isinstance(mobject_or_point, Mobject):
point = mobject_or_point.get_critical_point(direction)
@ -2148,7 +2197,7 @@ class Mobject:
all_mobjects = [self] + list(it.chain(*sub_families))
return remove_list_redundancies(all_mobjects)
def family_members_with_points(self):
def family_members_with_points(self) -> list[Mobject]:
return [m for m in self.get_family() if m.get_num_points() > 0]
def arrange(
@ -2739,7 +2788,7 @@ class Mobject:
self,
z_index_value: float,
family: bool = True,
) -> VMobject:
) -> T:
"""Sets the :class:`~.Mobject`'s :attr:`z_index` to the value specified in `z_index_value`.
Parameters

View file

@ -29,7 +29,7 @@ class OpenGLImageMobject(OpenGLTexturedSurface):
):
self.image = filename_or_array
self.resampling_algorithm = resampling_algorithm
if type(filename_or_array) == np.ndarray:
if isinstance(filename_or_array, np.ndarray):
self.size = self.image.shape[1::-1]
elif isinstance(filename_or_array, (str, Path)):
path = get_full_raster_image_path(filename_or_array)

View file

@ -13,7 +13,6 @@ from typing import TYPE_CHECKING
import moderngl
import numpy as np
from colour import Color
from manim import config, logger
from manim.constants import *
@ -23,7 +22,6 @@ from manim.event_handler.event_type import EventType
from manim.renderer.shader_wrapper import ShaderWrapper, get_colormap_code
from manim.utils.bezier import integer_interpolate, interpolate
from manim.utils.color import *
from manim.utils.color import Colors, get_colormap_list
from manim.utils.deprecation import deprecated
# from ..utils.iterables import batch_by_property
@ -153,6 +151,7 @@ class OpenGLMobject:
self.init_updaters()
self.init_event_listeners()
self.init_points()
self.color = ManimColor.parse(color)
self.init_colors()
self.init_shader_data()
@ -204,10 +203,10 @@ class OpenGLMobject:
>>> from manim import Square, GREEN
>>> Square.set_default(color=GREEN, fill_opacity=0.25)
>>> s = Square(); s.color, s.fill_opacity
(<Color #83c167>, 0.25)
(ManimColor('#83C167'), 0.25)
>>> Square.set_default()
>>> s = Square(); s.color, s.fill_opacity
(<Color white>, 0.0)
(ManimColor('#FFFFFF'), 0.0)
.. manim:: ChangedDefaultTextcolor
:save_last_frame:
@ -2265,12 +2264,12 @@ class OpenGLMobject:
mob.data[name][:, 3] = resize_array(opacities, size) # type: ignore
return self
def set_color(self, color, opacity=None, recurse=True):
def set_color(self, color: ParsableManimColor | None, opacity=None, recurse=True):
self.set_rgba_array(color, opacity, recurse=False)
# Recurse to submobjects differently from how set_rgba_array
# in case they implement set_color differently
if color is not None:
self.color = Color(color)
self.color: ManimColor = ManimColor.parse(color)
if opacity is not None:
self.opacity = opacity
if recurse:
@ -2342,7 +2341,7 @@ class OpenGLMobject:
# Background rectangle
def add_background_rectangle(
self, color: Colors | None = None, opacity: float = 0.75, **kwargs
self, color: ParsableManimColor | None = None, opacity: float = 0.75, **kwargs
):
# TODO, this does not behave well when the mobject has points,
# since it gets displayed on top
@ -3192,7 +3191,7 @@ class OpenGLMobject:
class OpenGLGroup(OpenGLMobject):
def __init__(self, *mobjects, **kwargs):
if not all([isinstance(m, OpenGLMobject) for m in mobjects]):
if not all(isinstance(m, OpenGLMobject) for m in mobjects):
raise Exception("All submobjects must be of type OpenGLMobject")
super().__init__(**kwargs)
self.add(*mobjects)

View file

@ -162,7 +162,7 @@ class OpenGLPMobject(OpenGLMobject):
class OpenGLPGroup(OpenGLPMobject):
def __init__(self, *pmobs, **kwargs):
if not all([isinstance(m, OpenGLPMobject) for m in pmobs]):
if not all(isinstance(m, OpenGLPMobject) for m in pmobs):
raise Exception("All submobjects must be of type OpenglPMObject")
super().__init__(**kwargs)
self.add(*pmobs)

View file

@ -261,7 +261,9 @@ class OpenGLSurface(OpenGLMobject):
shader_data["du_point"] = du_points
shader_data["dv_point"] = dv_points
if self.colorscale:
shader_data["color"] = self._get_color_by_value(s_points)
if not hasattr(self, "color_by_val"):
self.color_by_val = self._get_color_by_value(s_points)
shader_data["color"] = self.color_by_val
else:
self.fill_in_shader_color_info(shader_data)
return shader_data
@ -378,7 +380,7 @@ class OpenGLTexturedSurface(OpenGLSurface):
if not isinstance(uv_surface, OpenGLSurface):
raise Exception("uv_surface must be of type OpenGLSurface")
if type(image_file) == np.ndarray:
if isinstance(image_file, np.ndarray):
image_file = change_to_rgba_array(image_file)
# Set texture information

View file

@ -7,7 +7,6 @@ from typing import TYPE_CHECKING
import moderngl
import numpy as np
from colour import Color
from manim import config
from manim.constants import *
@ -91,10 +90,9 @@ class OpenGLVMobject(OpenGLMobject):
def __init__(
self,
color: Color | None = None,
fill_color: Color | None = None,
fill_color: ParsableManimColor | None = None,
fill_opacity: float = 0.0,
stroke_color: Color | None = None,
stroke_color: ParsableManimColor | None = None,
stroke_opacity: float = 1.0,
stroke_width: float = DEFAULT_STROKE_WIDTH,
draw_stroke_behind_fill: bool = False,
@ -122,6 +120,12 @@ class OpenGLVMobject(OpenGLMobject):
self.triangulation = np.zeros(0, dtype="i4")
super().__init__(**kwargs)
self.refresh_unit_normal()
if fill_color is not None:
self.fill_color = ManimColor.parse(fill_color)
if stroke_color is not None:
self.stroke_color = ManimColor.parse(stroke_color)
def get_group_class(self):
return OpenGLVGroup
@ -220,8 +224,8 @@ class OpenGLVMobject(OpenGLMobject):
def set_fill(
self,
color: Color | Iterable[Color] | None = None,
opacity: float | Iterable[float] | None = None,
color: ParsableManimColor | None = None,
opacity: float | None = None,
recurse: bool = True,
) -> Self:
"""Set the fill color and fill opacity of a :class:`OpenGLVMobject`.
@ -398,14 +402,15 @@ class OpenGLVMobject(OpenGLMobject):
)
return self
def get_fill_colors(self) -> list[str]:
return [rgb_to_hex(rgba[:3]) for rgba in self.data["fill_rgba"]]
# Todo im not quite sure why we are doing this
def get_fill_colors(self):
return [ManimColor.from_rgb(rgba[:3]) for rgba in self.fill_rgba]
def get_fill_opacities(self) -> np.ndarray:
return self.data["fill_rgba"][:, 3]
def get_stroke_colors(self) -> list[str]:
return [rgb_to_hex(rgba[:3]) for rgba in self.data["stroke_rgba"]]
def get_stroke_colors(self):
return [ManimColor.from_rgb(rgba[:3]) for rgba in self.stroke_rgba]
def get_stroke_opacities(self) -> np.ndarray:
return self.data["stroke_rgba"][:, 3]
@ -1715,7 +1720,7 @@ class OpenGLVGroup(OpenGLVMobject):
"""
def __init__(self, *vmobjects, **kwargs):
if not all([isinstance(m, OpenGLVMobject) for m in vmobjects]):
if not all(isinstance(m, OpenGLVMobject) for m in vmobjects):
raise Exception("All submobjects must be of type OpenGLVMobject")
super().__init__(**kwargs)
self.add(*vmobjects)
@ -1919,7 +1924,8 @@ class OpenGLDashedVMobject(OpenGLVMobject):
self,
vmobject: OpenGLVMobject,
num_dashes: int = 15,
positive_space_ratio: float = 0.5,
dashed_ratio: float = 0.5,
color: ParsableManimColor = WHITE,
**kwargs,
):
super().__init__(**kwargs)

View file

@ -156,24 +156,47 @@ class Brace(VMobjectFromSVGPath):
class BraceLabel(VMobject, metaclass=ConvertToOpenGL):
"""Create a brace with a label attached.
Parameters
----------
obj
The mobject adjacent to which the brace is placed.
text
The label text.
brace_direction
The direction of the brace. By default ``DOWN``.
label_constructor
A class or function used to construct a mobject representing
the label. By default :class:`~.MathTex`.
font_size
The font size of the label, passed to the ``label_constructor``.
buff
The buffer between the mobject and the brace.
brace_config
Arguments to be passed to :class:`.Brace`.
kwargs
Additional arguments to be passed to :class:`~.VMobject`.
"""
def __init__(
self,
obj,
text,
brace_direction=DOWN,
label_constructor=MathTex,
font_size=DEFAULT_FONT_SIZE,
buff=0.2,
obj: Mobject,
text: str,
brace_direction: np.ndarray = DOWN,
label_constructor: type = MathTex,
font_size: float = DEFAULT_FONT_SIZE,
buff: float = 0.2,
brace_config: dict | None = None,
**kwargs,
):
self.label_constructor = label_constructor
super().__init__(**kwargs)
self.brace_direction = brace_direction
self.buff = buff
if isinstance(obj, list):
obj = self.get_group_class()(*obj)
self.brace = Brace(obj, brace_direction, buff, **kwargs)
if brace_config is None:
brace_config = {}
self.brace = Brace(obj, brace_direction, buff, **brace_config)
if isinstance(text, (tuple, list)):
self.label = self.label_constructor(font_size=font_size, *text, **kwargs)
@ -312,10 +335,12 @@ class ArcBrace(Brace):
def __init__(
self,
arc: Arc = Arc(start_angle=-1, angle=2, radius=1),
arc: Arc | None = None,
direction: Sequence[float] = RIGHT,
**kwargs,
):
if arc is None:
arc = Arc(start_angle=-1, angle=2, radius=1)
arc_end_angle = arc.start_angle + arc.angle
line = Line(UP * arc.start_angle, UP * arc_end_angle)
scale_shift = RIGHT * np.log(arc.radius)

View file

@ -12,6 +12,7 @@ import svgelements as se
from manim import config, logger
from ...constants import RIGHT
from ...utils.bezier import get_quadratic_approximation_of_cubic
from ...utils.images import get_full_vector_image_path
from ...utils.iterables import hash_obj
from ..geometry.arc import Circle
@ -81,6 +82,12 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
A dictionary with keyword arguments passed to
:class:`.VMobjectFromSVGPath` used for importing path elements.
If ``None`` (the default), no additional arguments are passed.
use_svg_cache
If True (default), the svg inputs (e.g. file_name, settings)
will be used as a key and a copy of the created mobject will
be saved using that key to be quickly retrieved if the same
inputs need be processed later. For large SVGs which are used
only once, this can be omitted to improve performance.
kwargs
Further arguments passed to the parent class.
"""
@ -100,6 +107,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
stroke_width: float | None = None,
svg_default: dict | None = None,
path_string_config: dict | None = None,
use_svg_cache: bool = True,
**kwargs,
):
super().__init__(color=None, stroke_color=None, fill_color=None, **kwargs)
@ -134,7 +142,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
path_string_config = {}
self.path_string_config = path_string_config
self.init_svg_mobject()
self.init_svg_mobject(use_svg_cache=use_svg_cache)
self.set_style(
fill_color=fill_color,
@ -145,7 +153,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
)
self.move_into_position()
def init_svg_mobject(self) -> None:
def init_svg_mobject(self, use_svg_cache: bool) -> None:
"""Checks whether the SVG has already been imported and
generates it if not.
@ -153,14 +161,16 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
--------
:meth:`.SVGMobject.generate_mobject`
"""
hash_val = hash_obj(self.hash_seed)
if hash_val in SVG_HASH_TO_MOB_MAP:
mob = SVG_HASH_TO_MOB_MAP[hash_val].copy()
self.add(*mob)
return
if use_svg_cache:
hash_val = hash_obj(self.hash_seed)
if hash_val in SVG_HASH_TO_MOB_MAP:
mob = SVG_HASH_TO_MOB_MAP[hash_val].copy()
self.add(*mob)
return
self.generate_mobject()
SVG_HASH_TO_MOB_MAP[hash_val] = self.copy()
if use_svg_cache:
SVG_HASH_TO_MOB_MAP[hash_val] = self.copy()
@property
def hash_seed(self) -> tuple:
@ -270,7 +280,7 @@ class SVGMobject(VMobject, metaclass=ConvertToOpenGL):
mob = self.polyline_to_mobject(shape)
elif isinstance(shape, se.Text):
mob = self.text_to_mobject(shape)
elif isinstance(shape, se.Use) or type(shape) == se.SVGElement:
elif isinstance(shape, se.Use) or type(shape) is se.SVGElement:
continue
else:
logger.warning(f"Unsupported element type: {type(shape)}")
@ -497,28 +507,96 @@ class VMobjectFromSVGPath(VMobject, metaclass=ConvertToOpenGL):
generate_points = init_points
def handle_commands(self) -> None:
segment_class_to_func_map = {
se.Move: (self.start_new_path, ("end",)),
se.Close: (self.close_path, ()),
se.Line: (self.add_line_to, ("end",)),
se.QuadraticBezier: (
self.add_quadratic_bezier_curve_to,
("control", "end"),
),
se.CubicBezier: (
self.add_cubic_bezier_curve_to,
("control1", "control2", "end"),
),
}
all_points: list[np.ndarray] = []
last_move = None
curve_start = None
# These lambdas behave the same as similar functions in
# vectorized_mobject, except they add to a list of points instead
# of updating this Mobject's numpy array of points. This way,
# we don't observe O(n^2) behavior for complex paths due to
# numpy's need to re-allocate memory on every append.
def move_pen(pt):
nonlocal last_move, curve_start
last_move = pt
if curve_start is None:
curve_start = last_move
if self.n_points_per_curve == 4:
def add_cubic(start, cp1, cp2, end):
nonlocal all_points
assert len(all_points) % 4 == 0, len(all_points)
all_points += [start, cp1, cp2, end]
move_pen(end)
def add_quad(start, cp, end):
add_cubic(start, (start + cp + cp) / 3, (cp + cp + end) / 3, end)
move_pen(end)
def add_line(start, end):
add_cubic(
start, (start + start + end) / 3, (start + end + end) / 3, end
)
move_pen(end)
else:
def add_cubic(start, cp1, cp2, end):
nonlocal all_points
assert len(all_points) % 3 == 0, len(all_points)
two_quads = get_quadratic_approximation_of_cubic(
start,
cp1,
cp2,
end,
)
all_points += two_quads[:3].tolist()
all_points += two_quads[3:].tolist()
move_pen(end)
def add_quad(start, cp, end):
nonlocal all_points
assert len(all_points) % 3 == 0, len(all_points)
all_points += [start, cp, end]
move_pen(end)
def add_line(start, end):
add_quad(start, (start + end) / 2, end)
move_pen(end)
for segment in self.path_obj:
segment_class = segment.__class__
func, attr_names = segment_class_to_func_map[segment_class]
points = [
_convert_point_to_3d(*segment.__getattribute__(attr_name))
for attr_name in attr_names
]
func(*points)
if segment_class == se.Move:
move_pen(_convert_point_to_3d(*segment.end))
elif segment_class == se.Line:
add_line(last_move, _convert_point_to_3d(*segment.end))
elif segment_class == se.QuadraticBezier:
add_quad(
last_move,
_convert_point_to_3d(*segment.control),
_convert_point_to_3d(*segment.end),
)
elif segment_class == se.CubicBezier:
add_cubic(
last_move,
_convert_point_to_3d(*segment.control1),
_convert_point_to_3d(*segment.control2),
_convert_point_to_3d(*segment.end),
)
elif segment_class == se.Close:
# If the SVG path naturally ends at the beginning of the curve,
# we do *not* need to draw a closing line. To account for floating
# point precision, we use a small value to compare the two points.
if abs(np.linalg.norm(last_move - curve_start)) > 0.0001:
add_line(last_move, curve_start)
curve_start = None
else:
raise AssertionError(f"Not implemented: {segment_class}")
# Get rid of the side effect of trailing "Z M" commands.
if self.has_new_path_started():
self.resize_points(self.get_num_points() - 1)
self.points = np.array(all_points, ndmin=2, dtype="float64")
# If we have no points, make sure the array is shaped properly
# (0 rows tall by 3 columns wide) so future operations can
# add or remove points correctly.
if len(all_points) == 0:
self.points = np.reshape(self.points, (0, 3))

View file

@ -67,8 +67,6 @@ __all__ = [
import itertools as it
from typing import Callable, Iterable, Sequence
from colour import Color
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import Polygon
from manim.mobject.geometry.shape_matchers import BackgroundRectangle
@ -82,12 +80,49 @@ from ..animation.composition import AnimationGroup
from ..animation.creation import Create, Write
from ..animation.fading import FadeIn
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..utils.color import BLACK, YELLOW
from ..utils.color import BLACK, YELLOW, ManimColor, ParsableManimColor
class Table(VGroup):
"""A mobject that displays a table on the screen.
Parameters
----------
table
A 2D array or list of lists. Content of the table has to be a valid input
for the callable set in ``element_to_mobject``.
row_labels
List of :class:`~.VMobject` representing the labels of each row.
col_labels
List of :class:`~.VMobject` representing the labels of each column.
top_left_entry
The top-left entry of the table, can only be specified if row and
column labels are given.
v_buff
Vertical buffer passed to :meth:`~.Mobject.arrange_in_grid`, by default 0.8.
h_buff
Horizontal buffer passed to :meth:`~.Mobject.arrange_in_grid`, by default 1.3.
include_outer_lines
``True`` if the table should include outer lines, by default False.
add_background_rectangles_to_entries
``True`` if background rectangles should be added to entries, by default ``False``.
entries_background_color
Background color of entries if ``add_background_rectangles_to_entries`` is ``True``.
include_background_rectangle
``True`` if the table should have a background rectangle, by default ``False``.
background_rectangle_color
Background color of table if ``include_background_rectangle`` is ``True``.
element_to_mobject
The :class:`~.Mobject` class applied to the table entries. by default :class:`~.Paragraph`. For common choices, see :mod:`~.text_mobject`/:mod:`~.tex_mobject`.
element_to_mobject_config
Custom configuration passed to :attr:`element_to_mobject`, by default {}.
arrange_in_grid_config
Dict passed to :meth:`~.Mobject.arrange_in_grid`, customizes the arrangement of the table.
line_config
Dict passed to :class:`~.Line`, customizes the lines of the table.
kwargs
Additional arguments to be passed to :class:`~.VGroup`.
Examples
--------
@ -159,9 +194,9 @@ class Table(VGroup):
h_buff: float = 1.3,
include_outer_lines: bool = False,
add_background_rectangles_to_entries: bool = False,
entries_background_color: Color = BLACK,
entries_background_color: ParsableManimColor = BLACK,
include_background_rectangle: bool = False,
background_rectangle_color: Color = BLACK,
background_rectangle_color: ParsableManimColor = BLACK,
element_to_mobject: Callable[
[float | str | VMobject],
VMobject,
@ -171,45 +206,6 @@ class Table(VGroup):
line_config: dict = {},
**kwargs,
):
"""
Parameters
----------
table
A 2D array or list of lists. Content of the table has to be a valid input
for the callable set in ``element_to_mobject``.
row_labels
List of :class:`~.VMobject` representing the labels of each row.
col_labels
List of :class:`~.VMobject` representing the labels of each column.
top_left_entry
The top-left entry of the table, can only be specified if row and
column labels are given.
v_buff
Vertical buffer passed to :meth:`~.Mobject.arrange_in_grid`, by default 0.8.
h_buff
Horizontal buffer passed to :meth:`~.Mobject.arrange_in_grid`, by default 1.3.
include_outer_lines
``True`` if the table should include outer lines, by default False.
add_background_rectangles_to_entries
``True`` if background rectangles should be added to entries, by default ``False``.
entries_background_color
Background color of entries if ``add_background_rectangles_to_entries`` is ``True``.
include_background_rectangle
``True`` if the table should have a background rectangle, by default ``False``.
background_rectangle_color
Background color of table if ``include_background_rectangle`` is ``True``.
element_to_mobject
The :class:`~.Mobject` class applied to the table entries. by default :class:`~.Paragraph`. For common choices, see :mod:`~.text_mobject`/:mod:`~.tex_mobject`.
element_to_mobject_config
Custom configuration passed to :attr:`element_to_mobject`, by default {}.
arrange_in_grid_config
Dict passed to :meth:`~.Mobject.arrange_in_grid`, customizes the arrangement of the table.
line_config
Dict passed to :class:`~.Line`, customizes the lines of the table.
kwargs
Additional arguments to be passed to :class:`~.VGroup`.
"""
self.row_labels = row_labels
self.col_labels = col_labels
self.top_left_entry = top_left_entry
@ -219,9 +215,9 @@ class Table(VGroup):
self.h_buff = h_buff
self.include_outer_lines = include_outer_lines
self.add_background_rectangles_to_entries = add_background_rectangles_to_entries
self.entries_background_color = entries_background_color
self.entries_background_color = ManimColor(entries_background_color)
self.include_background_rectangle = include_background_rectangle
self.background_rectangle_color = background_rectangle_color
self.background_rectangle_color = ManimColor(background_rectangle_color)
self.element_to_mobject = element_to_mobject
self.element_to_mobject_config = element_to_mobject_config
self.arrange_in_grid_config = arrange_in_grid_config
@ -505,7 +501,7 @@ class Table(VGroup):
"""
return VGroup(*(VGroup(*row) for row in self.mob_table))
def set_column_colors(self, *colors: Iterable[Color]) -> Table:
def set_column_colors(self, *colors: Iterable[ParsableManimColor]) -> Table:
"""Set individual colors for each column of the table.
Parameters
@ -534,7 +530,7 @@ class Table(VGroup):
column.set_color(color)
return self
def set_row_colors(self, *colors: Iterable[Color]) -> Table:
def set_row_colors(self, *colors: Iterable[ParsableManimColor]) -> Table:
"""Set individual colors for each row of the table.
Parameters
@ -753,10 +749,10 @@ class Table(VGroup):
label_group.add(*label)
return label_group
def add_background_to_entries(self, color: Color = BLACK) -> Table:
def add_background_to_entries(self, color: ParsableManimColor = BLACK) -> Table:
"""Adds a black :class:`~.BackgroundRectangle` to each entry of the table."""
for mob in self.get_entries():
mob.add_background_rectangle(color=color)
mob.add_background_rectangle(color=ManimColor(color))
return self
def get_cell(self, pos: Sequence[int] = (1, 1), **kwargs) -> Polygon:
@ -817,7 +813,7 @@ class Table(VGroup):
return rec
def get_highlighted_cell(
self, pos: Sequence[int] = (1, 1), color: Color = YELLOW, **kwargs
self, pos: Sequence[int] = (1, 1), color: ParsableManimColor = YELLOW, **kwargs
) -> BackgroundRectangle:
"""Returns a :class:`~.BackgroundRectangle` of the cell at the given position.
@ -849,11 +845,11 @@ class Table(VGroup):
self.add(table)
"""
cell = self.get_cell(pos)
bg_cell = BackgroundRectangle(cell, color=color, **kwargs)
bg_cell = BackgroundRectangle(cell, color=ManimColor(color), **kwargs)
return bg_cell
def add_highlighted_cell(
self, pos: Sequence[int] = (1, 1), color: Color = YELLOW, **kwargs
self, pos: Sequence[int] = (1, 1), color: ParsableManimColor = YELLOW, **kwargs
) -> Table:
"""Highlights one cell at a specific position on the table by adding a :class:`~.BackgroundRectangle`.
@ -883,7 +879,7 @@ class Table(VGroup):
table.add_highlighted_cell((2,2), color=GREEN)
self.add(table)
"""
bg_cell = self.get_highlighted_cell(pos, color=color, **kwargs)
bg_cell = self.get_highlighted_cell(pos, color=ManimColor(color), **kwargs)
self.add_to_back(bg_cell)
entry = self.get_entries(pos)
entry.background_rectangle = bg_cell

View file

@ -17,6 +17,7 @@ from pygments.formatters.html import HtmlFormatter
from pygments.lexers import get_lexer_by_name, guess_lexer_for_filename
from pygments.styles import get_all_styles
from manim import logger
from manim.constants import *
from manim.mobject.geometry.arc import Dot
from manim.mobject.geometry.polygram import RoundedRectangle
@ -101,7 +102,9 @@ class Code(VGroup):
font_size
A number which scales displayed code. Defaults to 24.
font
The name of the text font to be used. Defaults to ``"Monospac821 BT"``.
The name of the text font to be used. Defaults to ``"Monospace"``.
This is either a system font or one loaded with `text.register_font()`. Note
that font family names may be different across operating systems.
stroke_width
Stroke width for text. 0 is recommended, and the default.
margin
@ -131,6 +134,9 @@ class Code(VGroup):
'aliases or short names'.
generate_html_file
Defines whether to generate highlighted html code to the folder `assets/codes/generated_html_files`. Defaults to `False`.
warn_missing_font
If True (default), Manim will issue a warning if the font does not exist in the
(case-sensitive) list of fonts returned from `manimpango.list_fonts()`.
Attributes
----------
@ -159,7 +165,7 @@ class Code(VGroup):
tab_width: int = 3,
line_spacing: float = 0.3,
font_size: float = 24,
font: str = "Monospac821 BT",
font: str = "Monospace", # This should be in the font list on all platforms.
stroke_width: float = 0,
margin: float = 0.3,
indentation_chars: str = " ",
@ -173,6 +179,7 @@ class Code(VGroup):
style: str = "vim",
language: str | None = None,
generate_html_file: bool = False,
warn_missing_font: bool = True,
**kwargs,
):
super().__init__(
@ -183,6 +190,7 @@ class Code(VGroup):
self.background_stroke_width = background_stroke_width
self.tab_width = tab_width
self.line_spacing = line_spacing
self.warn_missing_font = warn_missing_font
self.font = font
self.font_size = font_size
self.margin = margin
@ -317,6 +325,7 @@ class Code(VGroup):
font=self.font,
disable_ligatures=True,
stroke_width=self.stroke_width,
warn_missing_font=self.warn_missing_font,
)
for i in line_numbers:
i.set_color(self.default_color)
@ -344,6 +353,7 @@ class Code(VGroup):
font=self.font,
disable_ligatures=True,
stroke_width=self.stroke_width,
warn_missing_font=self.warn_missing_font,
)
for line_no in range(code.__len__()):
line = code.chars[line_no]

View file

@ -66,7 +66,7 @@ class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
fill_opacity: float = 1.0,
**kwargs,
):
super().__init__(**kwargs)
super().__init__(**kwargs, stroke_width=stroke_width)
self.number = number
self.num_decimal_places = num_decimal_places
self.include_sign = include_sign
@ -78,7 +78,6 @@ class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
self.include_background_rectangle = include_background_rectangle
self.edge_to_fix = edge_to_fix
self._font_size = font_size
self.stroke_width = stroke_width
self.fill_opacity = fill_opacity
self.initial_config = kwargs.copy()
@ -174,7 +173,6 @@ class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
return num_string
def _string_to_mob(self, string: str, mob_class: VMobject | None = None, **kwargs):
if mob_class is None:
mob_class = self.mob_class
@ -405,7 +403,6 @@ class Variable(VMobject, metaclass=ConvertToOpenGL):
num_decimal_places: int = 2,
**kwargs,
):
self.label = MathTex(label) if isinstance(label, str) else label
equals = MathTex("=").next_to(self.label, RIGHT)
self.label.add(equals)

View file

@ -12,6 +12,8 @@ r"""Mobjects representing text rendered using LaTeX.
from __future__ import annotations
from manim.utils.color import ManimColor
__all__ = [
"SingleStringMathTex",
"MathTex",
@ -28,8 +30,6 @@ from functools import reduce
from textwrap import dedent
from typing import Dict, Iterable, Optional
from colour import Color
from manim import config, logger
from manim.constants import *
from manim.mobject.geometry.line import Line
@ -38,8 +38,6 @@ from manim.mobject.types.vectorized_mobject import VectorizedPoint, VGroup, VMob
from manim.utils.tex import TexTemplate
from manim.utils.tex_file_writing import tex_to_svg_file
SCALE_FACTOR_PER_FONT_POINT = 1 / 960
tex_string_to_mob_map = {}
@ -66,7 +64,6 @@ class SingleStringMathTex(SVGMobject):
font_size: float = DEFAULT_FONT_SIZE,
**kwargs,
):
if kwargs.get("color") is None:
# makes it so that color isn't explicitly passed for these mobs,
# and can instead inherit from the parent
@ -250,7 +247,7 @@ class MathTex(SingleStringMathTex):
*tex_strings,
arg_separator: str = " ",
substrings_to_isolate: Iterable[str] | None = None,
tex_to_color_map: dict[str, Color] = None,
tex_to_color_map: dict[str, ManimColor] = None,
tex_environment: str = "align*",
**kwargs,
):
@ -376,6 +373,29 @@ class MathTex(SingleStringMathTex):
part.set_color(color)
return self
def set_opacity_by_tex(
self, tex: str, opacity: float = 0.5, remaining_opacity: float = None, **kwargs
):
"""
Sets the opacity of the tex specified. If 'remaining_opacity' is specified,
then the remaining tex will be set to that opacity.
Parameters
----------
tex
The tex to set the opacity of.
opacity
Default 0.5. The opacity to set the tex to
remaining_opacity
Default None. The opacity to set the remaining tex to.
If None, then the remaining tex will not be changed
"""
if remaining_opacity is not None:
self.set_opacity(opacity=remaining_opacity)
for part in self.get_parts_by_tex(tex):
part.set_opacity(opacity)
return self
def set_color_by_tex_to_color_map(self, texs_to_color_map, **kwargs):
for texs, color in list(texs_to_color_map.items()):
try:
@ -427,7 +447,8 @@ class Tex(MathTex):
class BulletedList(Tex):
"""
"""A bulleted list.
Examples
--------
@ -480,7 +501,8 @@ class BulletedList(Tex):
class Title(Tex):
"""
"""A mobject representing an underlined title.
Examples
--------
.. manim:: TitleExample
@ -504,7 +526,6 @@ class Title(Tex):
underline_buff=MED_SMALL_BUFF,
**kwargs,
):
self.include_underline = include_underline
self.match_underline_width_to_text = match_underline_width_to_text
self.underline_buff = underline_buff

View file

@ -6,7 +6,7 @@
.. important::
See the corresponding tutorial :ref:`rendering-with-latex`
See the corresponding tutorial :ref:`using-text-objects`, especially for information about fonts.
The simplest way to add text to your animations is to use the :class:`~.Text` class. It uses the Pango library to render text.
@ -63,7 +63,6 @@ from typing import Iterable, Sequence
import manimpango
import numpy as np
from colour import Color
from manimpango import MarkupUtils, PangoUtils, TextSetting
from manim import config, logger
@ -71,7 +70,7 @@ from manim.constants import *
from manim.mobject.geometry.arc import Dot
from manim.mobject.svg.svg_mobject import SVGMobject
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.utils.color import Colors, color_gradient
from manim.utils.color import ManimColor, ParsableManimColor, color_gradient
from manim.utils.deprecation import deprecated
TEXT_MOB_SCALE_FACTOR = 0.05
@ -148,7 +147,7 @@ class Paragraph(VGroup):
self,
*text: Sequence[str],
line_spacing: float = -1,
alignment: Optional[str] = None,
alignment: str | None = None,
**kwargs,
) -> None:
self.line_spacing = line_spacing
@ -301,6 +300,13 @@ class Text(SVGMobject):
----------
text
The text that needs to be created as a mobject.
font
The font family to be used to render the text. This is either a system font or
one loaded with `register_font()`. Note that font family names may be different
across operating systems.
warn_missing_font
If True (default), Manim will issue a warning if the font does not exist in the
(case-sensitive) list of fonts returned from `manimpango.list_fonts()`.
Returns
-------
@ -406,7 +412,7 @@ class Text(SVGMobject):
text: str,
fill_opacity: float = 1.0,
stroke_width: float = 0,
color: Color | str | None = None,
color: ParsableManimColor | None = None,
font_size: float = DEFAULT_FONT_SIZE,
line_spacing: float = -1,
font: str = "",
@ -419,6 +425,7 @@ class Text(SVGMobject):
t2w: dict[str, str] = None,
gradient: tuple = None,
tab_width: int = 4,
warn_missing_font: bool = True,
# Mobject
height: float = None,
width: float = None,
@ -426,8 +433,11 @@ class Text(SVGMobject):
disable_ligatures: bool = False,
**kwargs,
) -> None:
self.line_spacing = line_spacing
if font and warn_missing_font:
fonts_list = manimpango.list_fonts()
if font not in fonts_list:
logger.warning(f"Font {font} not in {fonts_list}.")
self.font = font
self._font_size = float(font_size)
# needs to be a float or else size is inflated when font_size = 24
@ -471,7 +481,7 @@ class Text(SVGMobject):
else:
self.line_spacing = self._font_size + self._font_size * self.line_spacing
color = Color(color) if color else VMobject().color
color: ManimColor = ManimColor(color) if color else VMobject().color
file_name = self._text2svg(color)
PangoUtils.remove_last_M(file_name)
super().__init__(
@ -481,6 +491,7 @@ class Text(SVGMobject):
height=height,
width=width,
should_center=should_center,
use_svg_cache=False,
**kwargs,
)
self.text = text
@ -493,18 +504,58 @@ class Text(SVGMobject):
if len(each.points) == 0:
continue
points = each.points
last = points[0]
each.clear_points()
curve_start = points[0]
assert len(curve_start) == self.dim, curve_start
# Some of the glyphs in this text might not be closed,
# so we close them by identifying when one curve ends
# but it is not where the next curve starts.
# It is more efficient to temporarily create a list
# of points and add them one at a time, then turn them
# into a numpy array at the end, rather than creating
# new numpy arrays every time a point or fixing line
# is added (which is O(n^2) for numpy arrays).
closed_curve_points = []
# OpenGL has points be part of quadratic Bezier curves;
# Cairo uses cubic Bezier curves.
if nppc == 3: # RendererType.OPENGL
def add_line_to(end):
nonlocal closed_curve_points
start = closed_curve_points[-1]
closed_curve_points += [
start,
(start + end) / 2,
end,
]
else: # RendererType.CAIRO
def add_line_to(end):
nonlocal closed_curve_points
start = closed_curve_points[-1]
closed_curve_points += [
start,
(start + start + end) / 3,
(start + end + end) / 3,
end,
]
for index, point in enumerate(points):
each.append_points([point])
closed_curve_points.append(point)
if (
index != len(points) - 1
and (index + 1) % nppc == 0
and any(point != points[index + 1])
):
each.add_line_to(last)
last = points[index + 1]
each.add_line_to(last)
# Add straight line from last point on this curve to the
# start point on the next curve. We represent the line
# as a cubic bezier curve where the two control points
# are half-way between the start and stop point.
add_line_to(curve_start)
curve_start = points[index + 1]
# Make sure last curve is closed
add_line_to(curve_start)
each.points = np.array(closed_curve_points, ndmin=2)
# anti-aliasing
if height is None and width is None:
self.scale(TEXT_MOB_SCALE_FACTOR)
@ -591,10 +642,10 @@ class Text(SVGMobject):
for start, end in self._find_indexes(word, self.text):
self.chars[start:end].set_color_by_gradient(*gradient)
def _text2hash(self, color: Color):
def _text2hash(self, color: ManimColor):
"""Generates ``sha256`` hash for file name."""
settings = (
"PANGO" + self.font + self.slant + self.weight + color.hex_l
"PANGO" + self.font + self.slant + self.weight + str(color)
) # to differentiate Text and CairoText
settings += str(self.t2f) + str(self.t2s) + str(self.t2w) + str(self.t2c)
settings += str(self.line_spacing) + str(self._font_size)
@ -639,7 +690,9 @@ class Text(SVGMobject):
t2xwords = set(chain(*([*t2x.keys()] for t2x, _ in t2xs)))
for word in t2xwords:
setting_args = {
arg: t2x[word] if word in t2x else default_args[arg]
arg: str(t2x[word]) if word in t2x else default_args[arg]
# NOTE: when t2x[word] is a ManimColor, str will yield the
# hex representation
for t2x, arg in t2xs
}
@ -655,13 +708,13 @@ class Text(SVGMobject):
if self.gradient:
colors = color_gradient(self.gradient, len(self.text))
for i in range(len(self.text)):
args["color"] = colors[i].hex
args["color"] = colors[i].to_hex()
settings.append(TextSetting(i, i + 1, **args))
for word, gradient in self.t2g.items():
if isinstance(gradient, str) or len(gradient) == 1:
color = gradient if isinstance(gradient, str) else gradient[0]
gradient = [Color(color)]
gradient = [ManimColor(color)]
colors = (
color_gradient(gradient, len(word))
if len(gradient) != 1
@ -669,11 +722,11 @@ class Text(SVGMobject):
)
for start, end in self._find_indexes(word, self.text):
for i in range(start, end):
args["color"] = colors[i - start].hex
args["color"] = colors[i - start].to_hex()
settings.append(TextSetting(i, i + 1, **args))
return settings
def _text2settings(self, color: Color):
def _text2settings(self, color: str):
"""Converts the texts and styles to a setting for parsing."""
t2xs = [
(self.t2f, "font"),
@ -682,6 +735,7 @@ class Text(SVGMobject):
(self.t2c, "color"),
]
# setting_args requires values to be strings
default_args = {
arg: getattr(self, arg) if arg != "color" else str(color) for _, arg in t2xs
}
@ -739,7 +793,7 @@ class Text(SVGMobject):
return settings
def _text2svg(self, color: Color):
def _text2svg(self, color: ManimColor):
"""Convert the text to SVG using Pango."""
size = self._font_size
line_spacing = self.line_spacing
@ -905,7 +959,9 @@ class MarkupText(SVGMobject):
Global weight setting, e.g. `NORMAL` or `BOLD`. Local overrides are possible.
gradient
Global gradient setting. Local overrides are possible.
warn_missing_font
If True (default), Manim will issue a warning if the font does not exist in the
(case-sensitive) list of fonts returned from `manimpango.list_fonts()`.
Returns
-------
@ -1076,7 +1132,7 @@ class MarkupText(SVGMobject):
text: str,
fill_opacity: float = 1,
stroke_width: float = 0,
color: Color | None = None,
color: ParsableManimColor | None = None,
font_size: float = DEFAULT_FONT_SIZE,
line_spacing: int = -1,
font: str = "",
@ -1089,11 +1145,15 @@ class MarkupText(SVGMobject):
width: int = None,
should_center: bool = True,
disable_ligatures: bool = False,
warn_missing_font: bool = True,
**kwargs,
) -> None:
self.text = text
self.line_spacing = line_spacing
if font and warn_missing_font:
fonts_list = manimpango.list_fonts()
if font not in fonts_list:
logger.warning(f"Font {font} not in {fonts_list}.")
self.font = font
self._font_size = float(font_size)
self.slant = slant
@ -1125,7 +1185,7 @@ class MarkupText(SVGMobject):
else:
self.line_spacing = self._font_size + self._font_size * self.line_spacing
color = Color(color) if color else VMobject().color
color: ManimColor = ManimColor(color) if color else VMobject().color
file_name = self._text2svg(color)
PangoUtils.remove_last_M(file_name)
@ -1147,18 +1207,56 @@ class MarkupText(SVGMobject):
if len(each.points) == 0:
continue
points = each.points
last = points[0]
each.clear_points()
curve_start = points[0]
assert len(curve_start) == self.dim, curve_start
# Some of the glyphs in this text might not be closed,
# so we close them by identifying when one curve ends
# but it is not where the next curve starts.
# It is more efficient to temporarily create a list
# of points and add them one at a time, then turn them
# into a numpy array at the end, rather than creating
# new numpy arrays every time a point or fixing line
# is added (which is O(n^2) for numpy arrays).
closed_curve_points = []
# OpenGL has points be part of quadratic Bezier curves;
# Cairo uses cubic Bezier curves.
if nppc == 3: # RendererType.OPENGL
def add_line_to(end):
nonlocal closed_curve_points
start = closed_curve_points[-1]
closed_curve_points += [
start,
(start + end) / 2,
end,
]
else: # RendererType.CAIRO
def add_line_to(end):
nonlocal closed_curve_points
start = closed_curve_points[-1]
closed_curve_points += [
start,
(start + start + end) / 3,
(start + end + end) / 3,
end,
]
for index, point in enumerate(points):
each.append_points([point])
closed_curve_points.append(point)
if (
index != len(points) - 1
and (index + 1) % nppc == 0
and any(point != points[index + 1])
):
each.add_line_to(last)
last = points[index + 1]
each.add_line_to(last)
# Add straight line from last point on this curve to the
# start point on the next curve.
add_line_to(curve_start)
curve_start = points[index + 1]
# Make sure last curve is closed
add_line_to(curve_start)
each.points = np.array(closed_curve_points, ndmin=2)
if self.gradient:
self.set_color_by_gradient(*self.gradient)
@ -1203,10 +1301,14 @@ class MarkupText(SVGMobject):
else:
self.scale(font_val / self.font_size)
def _text2hash(self, color: Color):
def _text2hash(self, color: ManimColor):
"""Generates ``sha256`` hash for file name."""
settings = (
"MARKUPPANGO" + self.font + self.slant + self.weight + color.hex_l
"MARKUPPANGO"
+ self.font
+ self.slant
+ self.weight
+ ManimColor(color).to_hex().lower()
) # to differentiate from classical Pango Text
settings += str(self.line_spacing) + str(self._font_size)
settings += str(self.disable_ligatures)
@ -1216,7 +1318,7 @@ class MarkupText(SVGMobject):
hasher.update(id_str.encode())
return hasher.hexdigest()[:16]
def _text2svg(self, color: Color | None):
def _text2svg(self, color: ManimColor):
"""Convert the text to SVG using Pango."""
size = self._font_size
line_spacing = self.line_spacing
@ -1224,15 +1326,16 @@ class MarkupText(SVGMobject):
line_spacing /= TEXT2SVG_ADJUSTMENT_FACTOR
dir_name = config.get_dir("text_dir")
if not dir_name.exists():
if not dir_name.is_dir():
dir_name.mkdir(parents=True)
hash_name = self._text2hash(color)
file_name = dir_name / (hash_name + ".svg")
if file_name.exists():
svg_file = str(file_name.resolve())
else:
final_text = (
f'<span foreground="{color}">{self.text}</span>'
f'<span foreground="{color.to_hex()}">{self.text}</span>'
if color is not None
else self.text
)
@ -1310,7 +1413,7 @@ class MarkupText(SVGMobject):
if re.match("#[0-9a-f]{6}", col):
return col
else:
return Colors[col.lower()].value
return ManimColor(col).to_hex()
def _extract_color_tags(self):
"""Used to determine which parts (if any) of the string should be formatted

View file

@ -16,7 +16,7 @@ __all__ = [
"Torus",
]
from typing import TYPE_CHECKING
from typing import Callable, Sequence
import numpy as np
@ -28,15 +28,19 @@ from manim.mobject.mobject import *
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
from manim.utils.color import *
from manim.utils.color import (
BLUE,
BLUE_D,
BLUE_E,
LIGHT_GREY,
WHITE,
ManimColor,
ParsableManimColor,
interpolate_color,
)
from manim.utils.iterables import tuplify
from manim.utils.space_ops import normalize, perpendicular_bisector, z_to_vector
if TYPE_CHECKING:
from typing import *
from colour import Color
class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL):
def __init__(self, shade_in_3d: bool = True, **kwargs):
@ -102,10 +106,10 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
v_range: Sequence[float] = [0, 1],
resolution: Sequence[int] = 32,
surface_piece_config: dict = {},
fill_color: Color = BLUE_D,
fill_color: ParsableManimColor = BLUE_D,
fill_opacity: float = 1.0,
checkerboard_colors: Sequence[Color] = [BLUE_D, BLUE_E],
stroke_color: Color = LIGHT_GREY,
checkerboard_colors: Sequence[ParsableManimColor] | bool = [BLUE_D, BLUE_E],
stroke_color: ParsableManimColor = LIGHT_GREY,
stroke_width: float = 0.5,
should_make_jagged: bool = False,
pre_function_handle_to_anchor_scale_factor: float = 0.00001,
@ -116,10 +120,15 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
super().__init__(**kwargs)
self.resolution = resolution
self.surface_piece_config = surface_piece_config
self.fill_color = fill_color
self.fill_color: ManimColor = ManimColor(fill_color)
self.fill_opacity = fill_opacity
self.checkerboard_colors = checkerboard_colors
self.stroke_color = stroke_color
if checkerboard_colors:
self.checkerboard_colors: list[ManimColor] = [
ManimColor(x) for x in checkerboard_colors
]
else:
self.checkerboard_colors = checkerboard_colors
self.stroke_color: ManimColor = ManimColor(stroke_color)
self.stroke_width = stroke_width
self.should_make_jagged = should_make_jagged
self.pre_function_handle_to_anchor_scale_factor = (
@ -188,7 +197,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
self.set_fill_by_checkerboard(*self.checkerboard_colors)
def set_fill_by_checkerboard(
self, *colors: Sequence[Color], opacity: float = None
self, *colors: Sequence[ParsableManimColor], opacity: float = None
) -> Mobject:
"""Sets the fill_color of each face of :class:`Surface` in
an alternating pattern.
@ -215,7 +224,7 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
def set_fill_by_value(
self,
axes: Mobject,
colorscale: Union[Iterable[Color], Color] | None = None,
colorscale: list[ParsableManimColor] | ParsableManimColor | None = None,
axis: int = 2,
**kwargs,
) -> Mobject:
@ -446,8 +455,8 @@ class Dot3D(Sphere):
self,
point: list | np.ndarray = ORIGIN,
radius: float = DEFAULT_DOT_RADIUS,
color: Color = WHITE,
resolution: Sequence[int] = (8, 8),
color: ParsableManimColor = WHITE,
resolution=(8, 8),
**kwargs,
) -> None:
super().__init__(center=point, radius=radius, resolution=resolution, **kwargs)
@ -488,7 +497,7 @@ class Cube(VGroup):
self,
side_length: float = 2,
fill_opacity: float = 0.75,
fill_color: Color = BLUE,
fill_color: ParsableManimColor = BLUE,
stroke_width: float = 0,
**kwargs,
) -> None:
@ -688,7 +697,7 @@ class Cone(Surface):
Parameters
----------
direction : :class:`numpy.array`
direction
The direction of the apex.
"""
self.direction = direction
@ -900,7 +909,7 @@ class Line3D(Cylinder):
start: np.ndarray = LEFT,
end: np.ndarray = RIGHT,
thickness: float = 0.02,
color: Color = None,
color: ParsableManimColor = None,
**kwargs,
):
self.thickness = thickness
@ -1123,7 +1132,7 @@ class Arrow3D(Line3D):
thickness: float = 0.02,
height: float = 0.3,
base_radius: float = 0.08,
color: Color = WHITE,
color: ParsableManimColor = WHITE,
**kwargs,
) -> None:
super().__init__(

View file

@ -6,7 +6,6 @@ __all__ = ["AbstractImageMobject", "ImageMobject", "ImageMobjectFromCamera"]
import pathlib
import colour
import numpy as np
from PIL import Image
from PIL.Image import Resampling
@ -17,7 +16,7 @@ from ... import config
from ...constants import *
from ...mobject.mobject import Mobject
from ...utils.bezier import interpolate
from ...utils.color import WHITE, color_to_int_rgb
from ...utils.color import WHITE, ManimColor, color_to_int_rgb
from ...utils.images import change_to_rgba_array, get_full_raster_image_path
@ -84,6 +83,7 @@ class AbstractImageMobject(Mobject):
"Available algorithms: 'bicubic', 'nearest', 'box', 'bilinear', "
"'hamming', 'lanczos'.",
)
return self
def reset_points(self):
# Corresponding corners of image are fixed to these 3 points
@ -276,7 +276,7 @@ class ImageMobject(AbstractImageMobject):
def get_style(self):
return {
"fill_color": colour.rgb2hex(self.color.get_rgb()),
"fill_color": ManimColor(self.color.get_rgb()).to_hex(),
"fill_opacity": self.fill_opacity,
}

View file

@ -5,7 +5,6 @@ from __future__ import annotations
__all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot", "Point"]
import numpy as np
from colour import Color
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.mobject.opengl.opengl_point_cloud_mobject import OpenGLPMobject
@ -17,6 +16,7 @@ from ...utils.color import (
BLACK,
WHITE,
YELLOW,
ManimColor,
color_gradient,
color_to_rgba,
rgba_to_color,
@ -76,7 +76,7 @@ class PMobject(Mobject, metaclass=ConvertToOpenGL):
num_new_points = len(points)
self.points = np.append(self.points, points, axis=0)
if rgbas is None:
color = Color(color) if color else self.color
color = ManimColor(color) if color else self.color
rgbas = np.repeat([color_to_rgba(color, alpha)], num_new_points, axis=0)
elif len(rgbas) != len(points):
raise ValueError("points and rgbas must have same length")
@ -244,7 +244,8 @@ class Mobject2D(PMobject, metaclass=ConvertToOpenGL):
class PGroup(PMobject):
"""
"""A group for several point mobjects.
Examples
--------
@ -266,7 +267,7 @@ class PGroup(PMobject):
"""
def __init__(self, *pmobs, **kwargs):
if not all([isinstance(m, (PMobject, OpenGLPMobject)) for m in pmobs]):
if not all(isinstance(m, (PMobject, OpenGLPMobject)) for m in pmobs):
raise ValueError(
"All submobjects must be of type PMobject or OpenGLPMObject"
" if using the opengl renderer",
@ -281,7 +282,8 @@ class PGroup(PMobject):
class PointCloudDot(Mobject1D):
"""A disc made of a cloud of Dots
"""A disc made of a cloud of dots.
Examples
--------
.. manim:: PointCloudDotExample
@ -347,7 +349,7 @@ class PointCloudDot(Mobject1D):
class Point(PMobject):
"""
"""A mobject representing a point.
Examples
--------

View file

@ -15,9 +15,8 @@ __all__ = [
import itertools as it
import sys
import typing
from typing import Callable, Optional, Sequence, Union
from typing import Callable, Sequence
import colour
import numpy as np
from PIL.Image import Image
@ -38,8 +37,7 @@ from ...utils.bezier import (
partial_bezier_points,
proportions_along_bezier_curve_for_point,
)
from ...utils.color import BLACK, WHITE, color_to_rgba
from ...utils.deprecation import deprecated
from ...utils.color import BLACK, WHITE, ManimColor, ParsableManimColor
from ...utils.iterables import make_even, resize_array, stretch_array_to_length, tuplify
from ...utils.space_ops import rotate_vector, shoelace_direction
@ -81,12 +79,12 @@ class VMobject(Mobject):
def __init__(
self,
fill_color=None,
fill_color: ParsableManimColor | None = None,
fill_opacity=0.0,
stroke_color=None,
stroke_color: ParsableManimColor | None = None,
stroke_opacity=1.0,
stroke_width=DEFAULT_STROKE_WIDTH,
background_stroke_color=BLACK,
background_stroke_color: ParsableManimColor | None = BLACK,
background_stroke_opacity=1.0,
background_stroke_width=0,
sheen_factor=0.0,
@ -105,7 +103,10 @@ class VMobject(Mobject):
self.fill_opacity = fill_opacity
self.stroke_opacity = stroke_opacity
self.stroke_width = stroke_width
self.background_stroke_color = background_stroke_color
if background_stroke_color is not None:
self.background_stroke_color: ManimColor = ManimColor(
background_stroke_color
)
self.background_stroke_opacity = background_stroke_opacity
self.background_stroke_width = background_stroke_width
self.sheen_factor = sheen_factor
@ -123,11 +124,16 @@ class VMobject(Mobject):
self.tolerance_for_point_equality = tolerance_for_point_equality
self.n_points_per_cubic_curve = n_points_per_cubic_curve
super().__init__(**kwargs)
self.submobjects: list[VMobject]
if fill_color:
self.fill_color = fill_color
if stroke_color:
self.stroke_color = stroke_color
# TODO: Find where color overwrites are happening and remove the color doubling
# if "color" in kwargs:
# fill_color = kwargs["color"]
# stroke_color = kwargs["color"]
if fill_color is not None:
self.fill_color = ManimColor.parse(fill_color)
if stroke_color is not None:
self.stroke_color = ManimColor.parse(stroke_color)
# OpenGL compatibility
@property
@ -172,7 +178,7 @@ class VMobject(Mobject):
return self
def generate_rgbas_array(self, color, opacity):
def generate_rgbas_array(self, color: ManimColor | list[ManimColor], opacity):
"""
First arg can be either a color, or a tuple/list of colors.
Likewise, opacity can either be a float, or a tuple of floats.
@ -180,10 +186,12 @@ class VMobject(Mobject):
one color was passed in, a second slightly light color
will automatically be added for the gradient
"""
colors = [c if (c is not None) else BLACK for c in tuplify(color)]
opacities = [o if (o is not None) else 0 for o in tuplify(opacity)]
colors: list[ManimColor] = [
ManimColor(c) if (c is not None) else BLACK for c in tuplify(color)
]
opacities: list[float] = [o if (o is not None) else 0 for o in tuplify(opacity)]
rgbas = np.array(
[color_to_rgba(c, o) for c, o in zip(*make_even(colors, opacities))],
[c.to_rgba_with_alpha(o) for c, o in zip(*make_even(colors, opacities))],
)
sheen_factor = self.get_sheen_factor()
@ -194,7 +202,9 @@ class VMobject(Mobject):
rgbas = np.append(rgbas, light_rgbas, axis=0)
return rgbas
def update_rgbas_array(self, array_name, color=None, opacity=None):
def update_rgbas_array(
self, array_name, color: ManimColor | None = None, opacity=None
):
rgbas = self.generate_rgbas_array(color, opacity)
if not hasattr(self, array_name):
setattr(self, array_name, rgbas)
@ -217,7 +227,7 @@ class VMobject(Mobject):
def set_fill(
self,
color: str | None = None,
color: ParsableManimColor | None = None,
opacity: float | None = None,
family: bool = True,
):
@ -266,7 +276,7 @@ class VMobject(Mobject):
def set_stroke(
self,
color=None,
color: ParsableManimColor = None,
width=None,
opacity=None,
background=False,
@ -289,7 +299,10 @@ class VMobject(Mobject):
if opacity is not None:
setattr(self, opacity_name, opacity)
if color is not None and background:
self.background_stroke_color = color
if isinstance(color, (list, tuple)):
self.background_stroke_color = color
else:
self.background_stroke_color = ManimColor(color)
return self
def set_background_stroke(self, **kwargs):
@ -299,12 +312,12 @@ class VMobject(Mobject):
def set_style(
self,
fill_color=None,
fill_opacity=None,
stroke_color=None,
fill_color: ParsableManimColor | None = None,
fill_opacity: float | None = None,
stroke_color: ParsableManimColor | None = None,
stroke_width=None,
stroke_opacity=None,
background_stroke_color=None,
background_stroke_color: ParsableManimColor = None,
background_stroke_width=None,
background_stroke_opacity=None,
sheen_factor=None,
@ -341,10 +354,11 @@ class VMobject(Mobject):
"stroke_width": self.get_stroke_width(),
}
# TODO: FIX COLORS HERE
if simple:
ret["fill_color"] = colour.rgb2hex(self.get_fill_color().get_rgb())
ret["fill_color"] = self.get_fill_color()
ret["fill_opacity"] = self.get_fill_opacity()
ret["stroke_color"] = colour.rgb2hex(self.get_stroke_color().get_rgb())
ret["stroke_color"] = self.get_stroke_color()
else:
ret["fill_color"] = self.get_fill_colors()
ret["fill_opacity"] = self.get_fill_opacities()
@ -373,7 +387,7 @@ class VMobject(Mobject):
sm1.match_style(sm2)
return self
def set_color(self, color, family=True):
def set_color(self, color: ParsableManimColor, family=True):
self.set_fill(color, family=family)
self.set_stroke(color, family=family)
return self
@ -417,9 +431,10 @@ class VMobject(Mobject):
"""
return self.get_fill_opacities()[0]
# TODO: Does this just do a copy?
def get_fill_colors(self):
return [
colour.Color(rgb=rgba[:3]) if rgba.any() else None
ManimColor(rgba[:3]) if rgba.any() else None
for rgba in self.get_fill_rgbas()
]
@ -455,7 +470,7 @@ class VMobject(Mobject):
def get_stroke_colors(self, background=False):
return [
colour.Color(rgb=rgba[:3]) if rgba.any() else None
ManimColor(rgba[:3]) if rgba.any() else None
for rgba in self.get_stroke_rgbas(background)
]
@ -2345,6 +2360,69 @@ class CurvesAsSubmobjects(VGroup):
part.match_style(vmobject)
self.add(part)
def point_from_proportion(self, alpha: float) -> np.ndarray:
"""Gets the point at a proportion along the path of the :class:`CurvesAsSubmobjects`.
Parameters
----------
alpha
The proportion along the the path of the :class:`CurvesAsSubmobjects`.
Returns
-------
:class:`numpy.ndarray`
The point on the :class:`CurvesAsSubmobjects`.
Raises
------
:exc:`ValueError`
If ``alpha`` is not between 0 and 1.
:exc:`Exception`
If the :class:`CurvesAsSubmobjects` has no submobjects, or no submobject has points.
"""
if alpha < 0 or alpha > 1:
raise ValueError(f"Alpha {alpha} not between 0 and 1.")
self._throw_error_if_no_submobjects()
submobjs_with_pts = self._get_submobjects_with_points()
if alpha == 1:
return submobjs_with_pts[-1].points[-1]
submobjs_arc_lengths = tuple(
part.get_arc_length() for part in submobjs_with_pts
)
total_length = sum(submobjs_arc_lengths)
target_length = alpha * total_length
current_length = 0
for i, part in enumerate(submobjs_with_pts):
part_length = submobjs_arc_lengths[i]
if current_length + part_length >= target_length:
residue = (target_length - current_length) / part_length
return part.point_from_proportion(residue)
current_length += part_length
def _throw_error_if_no_submobjects(self):
if len(self.submobjects) == 0:
caller_name = sys._getframe(1).f_code.co_name
raise Exception(
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject with no submobjects"
)
def _get_submobjects_with_points(self):
submobjs_with_pts = tuple(
part for part in self.submobjects if len(part.points) > 0
)
if len(submobjs_with_pts) == 0:
caller_name = sys._getframe(1).f_code.co_name
raise Exception(
f"Cannot call CurvesAsSubmobjects.{caller_name} for a CurvesAsSubmobject whose submobjects have no points"
)
return submobjs_with_pts
class DashedVMobject(VMobject, metaclass=ConvertToOpenGL):
"""A :class:`VMobject` composed of dashes instead of lines.

View file

@ -14,7 +14,6 @@ from math import ceil, floor
from typing import Callable, Iterable, Sequence
import numpy as np
from colour import Color
from PIL import Image
from manim.animation.updaters.update import UpdateFromAlphaFunc
@ -29,7 +28,16 @@ from ..constants import OUT, RIGHT, UP, RendererType
from ..mobject.mobject import Mobject
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..utils.bezier import interpolate, inverse_interpolate
from ..utils.color import BLUE_E, GREEN, RED, YELLOW, color_to_rgb, rgb_to_color
from ..utils.color import (
BLUE_E,
GREEN,
RED,
YELLOW,
ManimColor,
ParsableManimColor,
color_to_rgb,
rgb_to_color,
)
from ..utils.rate_functions import ease_out_sine, linear
from ..utils.simple_functions import sigmoid
@ -65,11 +73,11 @@ class VectorField(VGroup):
def __init__(
self,
func: Callable[[np.ndarray], np.ndarray],
color: Color | None = None,
color: ParsableManimColor | None = None,
color_scheme: Callable[[np.ndarray], float] | None = None,
min_color_scheme_value: float = 0,
max_color_scheme_value: float = 2,
colors: Sequence[Color] = DEFAULT_SCALAR_FIELD_COLORS,
colors: Sequence[ParsableManimColor] = DEFAULT_SCALAR_FIELD_COLORS,
**kwargs,
):
super().__init__(**kwargs)
@ -106,7 +114,7 @@ class VectorField(VGroup):
self.pos_to_color = lambda pos: rgb_to_color(self.pos_to_rgb(pos))
else:
self.single_color = True
self.color = color
self.color = ManimColor.parse(color)
self.submob_movement_updater = None
@staticmethod
@ -408,7 +416,7 @@ class VectorField(VGroup):
self,
start: float,
end: float,
colors: Iterable,
colors: Iterable[ParsableManimColor],
):
"""
Generates a gradient of rgbas as a numpy array
@ -532,11 +540,11 @@ class ArrowVectorField(VectorField):
def __init__(
self,
func: Callable[[np.ndarray], np.ndarray],
color: Color | None = None,
color: ParsableManimColor | None = None,
color_scheme: Callable[[np.ndarray], float] | None = None,
min_color_scheme_value: float = 0,
max_color_scheme_value: float = 2,
colors: Sequence[Color] = DEFAULT_SCALAR_FIELD_COLORS,
colors: Sequence[ParsableManimColor] = DEFAULT_SCALAR_FIELD_COLORS,
# Determining Vector positions:
x_range: Sequence[float] = None,
y_range: Sequence[float] = None,
@ -612,7 +620,7 @@ class ArrowVectorField(VectorField):
The root point of the vector.
"""
output = np.asarray(self.func(point))
output = np.array(self.func(point))
norm = np.linalg.norm(output)
if norm != 0:
output *= self.length_func(norm) / norm
@ -706,11 +714,11 @@ class StreamLines(VectorField):
def __init__(
self,
func: Callable[[np.ndarray], np.ndarray],
color: Color | None = None,
color: ParsableManimColor | None = None,
color_scheme: Callable[[np.ndarray], float] | None = None,
min_color_scheme_value: float = 0,
max_color_scheme_value: float = 2,
colors: Sequence[Color] = DEFAULT_SCALAR_FIELD_COLORS,
colors: Sequence[ParsableManimColor] = DEFAULT_SCALAR_FIELD_COLORS,
# Determining stream line starting positions:
x_range: Sequence[float] = None,
y_range: Sequence[float] = None,

View file

@ -9,7 +9,7 @@ from .. import config, logger
__all__ = []
plugins_requested: list = config["plugins"]
plugins_requested: list[str] = config["plugins"]
if "" in plugins_requested:
plugins_requested.remove("")
for plugin in pkg_resources.iter_entry_points("manim.plugins"):

View file

@ -1,9 +1,4 @@
"""
plugins_flags.py
------------
Plugin Managing Utility.
"""
"""Plugin Managing Utility"""
from __future__ import annotations

View file

@ -313,7 +313,7 @@ class Mesh(Object3D):
else:
self.shader.context.disable(moderngl.DEPTH_TEST)
from moderngl.program_members import Attribute
from moderngl import Attribute
shader_attributes = []
for k, v in self.shader.shader_program._members.items():

View file

@ -325,9 +325,22 @@ class Scene:
mobject.update(dt)
def should_update_mobjects(self) -> bool:
return self.always_update_mobjects or any(
[len(mob.get_family_updaters()) > 0 for mob in self.mobjects]
)
"""
This is only called when a single Wait animation is played.
"""
wait_animation = self.animations[0]
if wait_animation.is_static_wait is None:
should_update = (
self.always_update_mobjects
or self.updaters
or wait_animation.stop_condition is not None
or any(
mob.has_time_based_updater()
for mob in self.get_mobject_family_members()
)
)
wait_animation.is_static_wait = not should_update
return not wait_animation.is_static_wait
def has_time_based_updaters(self) -> bool:
return any(
@ -384,16 +397,6 @@ class Scene:
self.add(*filter(lambda m: isinstance(m, Mobject), values))
return self
def replace(self, mobject: Mobject, *replacements: Mobject):
if mobject in self.mobjects:
index = self.mobjects.index(mobject)
self.mobjects = [
*self.mobjects[:index],
*replacements,
*self.mobjects[index + 1 :],
]
return self
def remove(self, *mobjects_to_remove: Mobject):
"""
Removes anything in mobjects from scenes mobject list, but in the event that one
@ -403,9 +406,161 @@ class Scene:
For example, if the scene includes Group(m1, m2, m3), and we call scene.remove(m1),
the desired behavior is for the scene to then include m2 and m3 (ungrouped).
"""
to_remove = set(extract_mobject_family_members(mobjects_to_remove))
new_mobjects, _ = recursive_mobject_remove(self.mobjects, to_remove)
self.mobjects = new_mobjects
if config.renderer == RendererType.OPENGL:
mobjects_to_remove = []
meshes_to_remove = set()
for mobject_or_mesh in mobjects:
if isinstance(mobject_or_mesh, Object3D):
meshes_to_remove.add(mobject_or_mesh)
else:
mobjects_to_remove.append(mobject_or_mesh)
self.mobjects = restructure_list_to_exclude_certain_family_members(
self.mobjects,
mobjects_to_remove,
)
self.meshes = list(
filter(lambda mesh: mesh not in set(meshes_to_remove), self.meshes),
)
return self
elif config.renderer == RendererType.CAIRO:
for list_name in "mobjects", "foreground_mobjects":
self.restructure_mobjects(mobjects, list_name, False)
return self
def replace(self, old_mobject: Mobject, new_mobject: Mobject) -> None:
"""Replace one mobject in the scene with another, preserving draw order.
If ``old_mobject`` is a submobject of some other Mobject (e.g. a
:class:`.Group`), the new_mobject will replace it inside the group,
without otherwise changing the parent mobject.
Parameters
----------
old_mobject
The mobject to be replaced. Must be present in the scene.
new_mobject
A mobject which must not already be in the scene.
"""
if old_mobject is None or new_mobject is None:
raise ValueError("Specified mobjects cannot be None")
def replace_in_list(
mobj_list: list[Mobject], old_m: Mobject, new_m: Mobject
) -> bool:
# We use breadth-first search because some Mobjects get very deep and
# we expect top-level elements to be the most common targets for replace.
for i in range(0, len(mobj_list)):
# Is this the old mobject?
if mobj_list[i] == old_m:
# If so, write the new object to the same spot and stop looking.
mobj_list[i] = new_m
return True
# Now check all the children of all these mobs.
for mob in mobj_list: # noqa: SIM110
if replace_in_list(mob.submobjects, old_m, new_m):
# If we found it in a submobject, stop looking.
return True
# If we did not find the mobject in the mobject list or any submobjects,
# (or the list was empty), indicate we did not make the replacement.
return False
# Make use of short-circuiting conditionals to check mobjects and then
# foreground_mobjects
replaced = replace_in_list(
self.mobjects, old_mobject, new_mobject
) or replace_in_list(self.foreground_mobjects, old_mobject, new_mobject)
if not replaced:
raise ValueError(f"Could not find {old_mobject} in scene")
def add_updater(self, func: Callable[[float], None]) -> None:
"""Add an update function to the scene.
The scene updater functions are run every frame,
and they are the last type of updaters to run.
.. WARNING::
When using the Cairo renderer, scene updaters that
modify mobjects are not detected in the same way
that mobject updaters are. To be more concrete,
a mobject only modified via a scene updater will
not necessarily be added to the list of *moving
mobjects* and thus might not be updated every frame.
TL;DR: Use mobject updaters to update mobjects.
Parameters
----------
func
The updater function. It takes a float, which is the
time difference since the last update (usually equal
to the frame rate).
See also
--------
:meth:`.Scene.remove_updater`
:meth:`.Scene.update_self`
"""
self.updaters.append(func)
def remove_updater(self, func: Callable[[float], None]) -> None:
"""Remove an update function from the scene.
Parameters
----------
func
The updater function to be removed.
See also
--------
:meth:`.Scene.add_updater`
:meth:`.Scene.update_self`
"""
self.updaters = [f for f in self.updaters if f is not func]
def restructure_mobjects(
self,
to_remove: Mobject,
mobject_list_name: str = "mobjects",
extract_families: bool = True,
):
"""
tl:wr
If your scene has a Group(), and you removed a mobject from the Group,
this dissolves the group and puts the rest of the mobjects directly
in self.mobjects or self.foreground_mobjects.
In cases where the scene contains a group, e.g. Group(m1, m2, m3), but one
of its submobjects is removed, e.g. scene.remove(m1), the list of mobjects
will be edited to contain other submobjects, but not m1, e.g. it will now
insert m2 and m3 to where the group once was.
Parameters
----------
to_remove
The Mobject to remove.
mobject_list_name
The list of mobjects ("mobjects", "foreground_mobjects" etc) to remove from.
extract_families
Whether the mobject's families should be recursively extracted.
Returns
-------
Scene
The Scene mobject with restructured Mobjects.
"""
if extract_families:
to_remove = extract_mobject_family_members(
to_remove,
use_z_index=self.renderer.camera.use_z_index,
)
_list = getattr(self, mobject_list_name)
new_list = self.get_restructured_mobject_list(_list, to_remove)
setattr(self, mobject_list_name, new_list)
def bring_to_front(self, *mobjects: Mobject):
self.add(*mobjects)
@ -531,7 +686,7 @@ class Scene:
kw["override_skip_animations"] = True
return self.get_time_progression(duration, **kw)
def pre_play(self):
def pre_play(self): # Doesn't exist in Main
if self.presenter_mode and self.num_plays == 0:
self.hold_loop()

View file

@ -439,6 +439,59 @@ class SceneFileWriter:
]
)
def combine_to_section_videos(self) -> None:
"""Concatenate partial movie files for each section."""
self.finish_last_section()
sections_index: list[dict[str, Any]] = []
for section in self.sections:
# only if section does want to be saved
if section.video is not None:
logger.info(f"Combining partial files for section '{section.name}'")
self.combine_files(
section.get_clean_partial_movie_files(),
self.sections_output_dir / section.video,
)
sections_index.append(section.get_dict(self.sections_output_dir))
with (self.sections_output_dir / f"{self.output_name}.json").open("w") as file:
json.dump(sections_index, file, indent=4)
def clean_cache(self):
"""Will clean the cache by removing the oldest partial_movie_files."""
cached_partial_movies = [
(self.partial_movie_directory / file_name)
for file_name in self.partial_movie_directory.iterdir()
if file_name != "partial_movie_file_list.txt"
]
if len(cached_partial_movies) > config["max_files_cached"]:
number_files_to_delete = (
len(cached_partial_movies) - config["max_files_cached"]
)
oldest_files_to_delete = sorted(
cached_partial_movies,
key=lambda path: path.stat().st_atime,
)[:number_files_to_delete]
for file_to_delete in oldest_files_to_delete:
file_to_delete.unlink()
logger.info(
f"The partial movie directory is full (> {config['max_files_cached']} files). Therefore, manim has removed the {number_files_to_delete} oldest file(s)."
" You can change this behaviour by changing max_files_cached in config.",
)
def flush_cache_directory(self):
"""Delete all the cached partial movie files"""
cached_partial_movies = [
self.partial_movie_directory / file_name
for file_name in self.partial_movie_directory.iterdir()
if file_name != "partial_movie_file_list.txt"
]
for f in cached_partial_movies:
f.unlink()
logger.info(
f"Cache flushed. {len(cached_partial_movies)} file(s) deleted in %(par_dir)s.",
{"par_dir": self.partial_movie_directory},
)
def open_file(self) -> None:
if self.quiet:
curr_stdout = sys.stdout

View file

@ -7,7 +7,6 @@ __all__ = ["VectorScene", "LinearTransformationScene"]
from typing import Callable
import numpy as np
from colour import Color
from manim.mobject.geometry.arc import Dot
from manim.mobject.geometry.line import Arrow, Line, Vector
@ -28,7 +27,17 @@ from ..mobject.matrix import Matrix
from ..mobject.mobject import Mobject
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..scene.scene import Scene
from ..utils.color import BLUE_D, GREEN_C, GREY, RED_C, WHITE, YELLOW
from ..utils.color import (
BLACK,
BLUE_D,
GREEN_C,
GREY,
RED_C,
WHITE,
YELLOW,
ManimColor,
ParsableManimColor,
)
from ..utils.rate_functions import rush_from, rush_into
from ..utils.space_ops import angle_of_vector
@ -558,11 +567,12 @@ class LinearTransformationScene(VectorScene):
.. manim:: LinearTransformationSceneExample
class LinearTransformationSceneExample(LinearTransformationScene):
def __init__(self):
def __init__(self, **kwargs):
LinearTransformationScene.__init__(
self,
show_coordinates=True,
leave_ghost_vectors=True,
*kwargs
)
def construct(self):
@ -580,12 +590,11 @@ class LinearTransformationScene(VectorScene):
show_coordinates: bool = False,
show_basis_vectors: bool = True,
basis_vector_stroke_width: float = 6,
i_hat_color: Color = X_COLOR,
j_hat_color: Color = Y_COLOR,
i_hat_color: ParsableManimColor = X_COLOR,
j_hat_color: ParsableManimColor = Y_COLOR,
leave_ghost_vectors: bool = False,
**kwargs,
):
super().__init__(**kwargs)
self.include_background_plane = include_background_plane
@ -593,8 +602,8 @@ class LinearTransformationScene(VectorScene):
self.show_coordinates = show_coordinates
self.show_basis_vectors = show_basis_vectors
self.basis_vector_stroke_width = basis_vector_stroke_width
self.i_hat_color = i_hat_color
self.j_hat_color = j_hat_color
self.i_hat_color = ManimColor(i_hat_color)
self.j_hat_color = ManimColor(j_hat_color)
self.leave_ghost_vectors = leave_ghost_vectors
self.background_plane_kwargs = {
"color": GREY,

View file

@ -1,579 +0,0 @@
"""Colors and utility functions for conversion between different color models."""
from __future__ import annotations
from manim.utils.iterables import resize_with_interpolation
__all__ = [
"color_to_rgb",
"color_to_rgba",
"rgb_to_color",
"rgba_to_color",
"rgb_to_hex",
"hex_to_rgb",
"invert_color",
"color_to_int_rgb",
"color_to_int_rgba",
"color_gradient",
"interpolate_color",
"average_color",
"random_bright_color",
"random_color",
"get_shaded_rgb",
]
import random
from enum import Enum
from typing import Iterable
import numpy as np
from colour import Color
from ..utils.bezier import interpolate
from ..utils.space_ops import normalize
class Colors(Enum):
"""A list of pre-defined colors.
Examples
--------
.. manim:: ColorsOverview
:save_last_frame:
:hide_source:
from manim.utils.color import Colors
class ColorsOverview(Scene):
def construct(self):
def color_group(color):
group = VGroup(
*[
Line(ORIGIN, RIGHT * 1.5, stroke_width=35, color=Colors[name].value)
for name in subnames(color)
]
).arrange_submobjects(buff=0.4, direction=DOWN)
name = Text(color).scale(0.6).next_to(group, UP, buff=0.3)
if any(decender in color for decender in "gjpqy"):
name.shift(DOWN * 0.08)
group.add(name)
return group
def subnames(name):
return [name + "_" + char for char in "abcde"]
color_groups = VGroup(
*[
color_group(color)
for color in [
"blue",
"teal",
"green",
"yellow",
"gold",
"red",
"maroon",
"purple",
]
]
).arrange_submobjects(buff=0.2, aligned_edge=DOWN)
for line, char in zip(color_groups[0], "abcde"):
color_groups.add(Text(char).scale(0.6).next_to(line, LEFT, buff=0.2))
def named_lines_group(length, colors, names, text_colors, align_to_block):
lines = VGroup(
*[
Line(
ORIGIN,
RIGHT * length,
stroke_width=55,
color=Colors[color].value,
)
for color in colors
]
).arrange_submobjects(buff=0.6, direction=DOWN)
for line, name, color in zip(lines, names, text_colors):
line.add(Text(name, color=color).scale(0.6).move_to(line))
lines.next_to(color_groups, DOWN, buff=0.5).align_to(
color_groups[align_to_block], LEFT
)
return lines
other_colors = (
"pink",
"light_pink",
"orange",
"light_brown",
"dark_brown",
"gray_brown",
)
other_lines = named_lines_group(
3.2,
other_colors,
other_colors,
[BLACK] * 4 + [WHITE] * 2,
0,
)
gray_lines = named_lines_group(
6.6,
["white"] + subnames("gray") + ["black"],
[
"white",
"lighter_gray / gray_a",
"light_gray / gray_b",
"gray / gray_c",
"dark_gray / gray_d",
"darker_gray / gray_e",
"black",
],
[BLACK] * 3 + [WHITE] * 4,
2,
)
pure_colors = (
"pure_red",
"pure_green",
"pure_blue",
)
pure_lines = named_lines_group(
3.2,
pure_colors,
pure_colors,
[BLACK, BLACK, WHITE],
6,
)
self.add(color_groups, other_lines, gray_lines, pure_lines)
VGroup(*self.mobjects).move_to(ORIGIN)
The preferred way of using these colors is by importing their constants from manim:
.. code-block:: pycon
>>> from manim import RED, GREEN, BLUE
>>> RED
'#FC6255'
Note this way uses the name of the colors in UPPERCASE.
Alternatively, you can also import this Enum directly and use its members
directly, through the use of :code:`color.value`. Note this way uses the
name of the colors in lowercase.
.. code-block:: pycon
>>> from manim.utils.color import Colors
>>> Colors.red.value
'#FC6255'
.. note::
The colors of type "C" have an alias equal to the colorname without a letter,
e.g. GREEN = GREEN_C
"""
white: str = "#FFFFFF"
gray_a: str = "#DDDDDD"
gray_b: str = "#BBBBBB"
gray_c: str = "#888888"
gray_d: str = "#444444"
gray_e: str = "#222222"
black: str = "#000000"
lighter_gray: str = gray_a
light_gray: str = gray_b
gray: str = gray_c
dark_gray: str = gray_d
darker_gray: str = gray_e
blue_a: str = "#C7E9F1"
blue_b: str = "#9CDCEB"
blue_c: str = "#58C4DD"
blue_d: str = "#29ABCA"
blue_e: str = "#236B8E"
pure_blue: str = "#0000FF"
blue: str = blue_c
dark_blue: str = blue_e
teal_a: str = "#ACEAD7"
teal_b: str = "#76DDC0"
teal_c: str = "#5CD0B3"
teal_d: str = "#55C1A7"
teal_e: str = "#49A88F"
teal: str = teal_c
green_a: str = "#C9E2AE"
green_b: str = "#A6CF8C"
green_c: str = "#83C167"
green_d: str = "#77B05D"
green_e: str = "#699C52"
pure_green: str = "#00FF00"
green: str = green_c
yellow_a: str = "#FFF1B6"
yellow_b: str = "#FFEA94"
yellow_c: str = "#FFFF00"
yellow_d: str = "#F4D345"
yellow_e: str = "#E8C11C"
yellow: str = yellow_c
gold_a: str = "#F7C797"
gold_b: str = "#F9B775"
gold_c: str = "#F0AC5F"
gold_d: str = "#E1A158"
gold_e: str = "#C78D46"
gold: str = gold_c
red_a: str = "#F7A1A3"
red_b: str = "#FF8080"
red_c: str = "#FC6255"
red_d: str = "#E65A4C"
red_e: str = "#CF5044"
pure_red: str = "#FF0000"
red: str = red_c
maroon_a: str = "#ECABC1"
maroon_b: str = "#EC92AB"
maroon_c: str = "#C55F73"
maroon_d: str = "#A24D61"
maroon_e: str = "#94424F"
maroon: str = maroon_c
purple_a: str = "#CAA3E8"
purple_b: str = "#B189C6"
purple_c: str = "#9A72AC"
purple_d: str = "#715582"
purple_e: str = "#644172"
purple: str = purple_c
pink: str = "#D147BD"
light_pink: str = "#DC75CD"
orange: str = "#FF862F"
light_brown: str = "#CD853F"
dark_brown: str = "#8B4513"
gray_brown: str = "#736357"
def print_constant_definitions():
"""
A simple function used to generate the constant values below. To run it
paste this function and the Colors class into a file and run them.
"""
constants_names: list[str] = []
for name in Colors.__members__.keys():
name_upper = name.upper()
constants_names.append(name_upper)
print(f"{name_upper} = Colors.{name}")
if "GRAY" in name_upper:
name_upper = name_upper.replace("GRAY", "GREY")
constants_names.append(name_upper)
print(f"{name_upper} = Colors.{name}")
constants_names_repr = '[\n "' + '",\n "'.join(constants_names) + '",\n]'
print(f"\n__all__ += {constants_names_repr}")
WHITE: str = "#FFFFFF"
GRAY_A: str = "#DDDDDD"
GREY_A: str = "#DDDDDD"
GRAY_B: str = "#BBBBBB"
GREY_B: str = "#BBBBBB"
GRAY_C: str = "#888888"
GREY_C: str = "#888888"
GRAY_D: str = "#444444"
GREY_D: str = "#444444"
GRAY_E: str = "#222222"
GREY_E: str = "#222222"
BLACK: str = "#000000"
LIGHTER_GRAY: str = "#DDDDDD"
LIGHTER_GREY: str = "#DDDDDD"
LIGHT_GRAY: str = "#BBBBBB"
LIGHT_GREY: str = "#BBBBBB"
GRAY: str = "#888888"
GREY: str = "#888888"
DARK_GRAY: str = "#444444"
DARK_GREY: str = "#444444"
DARKER_GRAY: str = "#222222"
DARKER_GREY: str = "#222222"
BLUE_A: str = "#C7E9F1"
BLUE_B: str = "#9CDCEB"
BLUE_C: str = "#58C4DD"
BLUE_D: str = "#29ABCA"
BLUE_E: str = "#236B8E"
PURE_BLUE: str = "#0000FF"
BLUE: str = "#58C4DD"
DARK_BLUE: str = "#236B8E"
TEAL_A: str = "#ACEAD7"
TEAL_B: str = "#76DDC0"
TEAL_C: str = "#5CD0B3"
TEAL_D: str = "#55C1A7"
TEAL_E: str = "#49A88F"
TEAL: str = "#5CD0B3"
GREEN_A: str = "#C9E2AE"
GREEN_B: str = "#A6CF8C"
GREEN_C: str = "#83C167"
GREEN_D: str = "#77B05D"
GREEN_E: str = "#699C52"
PURE_GREEN: str = "#00FF00"
GREEN: str = "#83C167"
YELLOW_A: str = "#FFF1B6"
YELLOW_B: str = "#FFEA94"
YELLOW_C: str = "#FFFF00"
YELLOW_D: str = "#F4D345"
YELLOW_E: str = "#E8C11C"
YELLOW: str = "#FFFF00"
GOLD_A: str = "#F7C797"
GOLD_B: str = "#F9B775"
GOLD_C: str = "#F0AC5F"
GOLD_D: str = "#E1A158"
GOLD_E: str = "#C78D46"
GOLD: str = "#F0AC5F"
RED_A: str = "#F7A1A3"
RED_B: str = "#FF8080"
RED_C: str = "#FC6255"
RED_D: str = "#E65A4C"
RED_E: str = "#CF5044"
PURE_RED: str = "#FF0000"
RED: str = "#FC6255"
MAROON_A: str = "#ECABC1"
MAROON_B: str = "#EC92AB"
MAROON_C: str = "#C55F73"
MAROON_D: str = "#A24D61"
MAROON_E: str = "#94424F"
MAROON: str = "#C55F73"
PURPLE_A: str = "#CAA3E8"
PURPLE_B: str = "#B189C6"
PURPLE_C: str = "#9A72AC"
PURPLE_D: str = "#715582"
PURPLE_E: str = "#644172"
PURPLE: str = "#9A72AC"
PINK: str = "#D147BD"
LIGHT_PINK: str = "#DC75CD"
ORANGE: str = "#FF862F"
LIGHT_BROWN: str = "#CD853F"
DARK_BROWN: str = "#8B4513"
GRAY_BROWN: str = "#736357"
GREY_BROWN: str = "#736357"
__all__ += [
"WHITE",
"GRAY_A",
"GREY_A",
"GRAY_B",
"GREY_B",
"GRAY_C",
"GREY_C",
"GRAY_D",
"GREY_D",
"GRAY_E",
"GREY_E",
"BLACK",
"LIGHTER_GRAY",
"LIGHTER_GREY",
"LIGHT_GRAY",
"LIGHT_GREY",
"GRAY",
"GREY",
"DARK_GRAY",
"DARK_GREY",
"DARKER_GRAY",
"DARKER_GREY",
"BLUE_A",
"BLUE_B",
"BLUE_C",
"BLUE_D",
"BLUE_E",
"PURE_BLUE",
"BLUE",
"DARK_BLUE",
"TEAL_A",
"TEAL_B",
"TEAL_C",
"TEAL_D",
"TEAL_E",
"TEAL",
"GREEN_A",
"GREEN_B",
"GREEN_C",
"GREEN_D",
"GREEN_E",
"PURE_GREEN",
"GREEN",
"YELLOW_A",
"YELLOW_B",
"YELLOW_C",
"YELLOW_D",
"YELLOW_E",
"YELLOW",
"GOLD_A",
"GOLD_B",
"GOLD_C",
"GOLD_D",
"GOLD_E",
"GOLD",
"RED_A",
"RED_B",
"RED_C",
"RED_D",
"RED_E",
"PURE_RED",
"RED",
"MAROON_A",
"MAROON_B",
"MAROON_C",
"MAROON_D",
"MAROON_E",
"MAROON",
"PURPLE_A",
"PURPLE_B",
"PURPLE_C",
"PURPLE_D",
"PURPLE_E",
"PURPLE",
"PINK",
"LIGHT_PINK",
"ORANGE",
"LIGHT_BROWN",
"DARK_BROWN",
"GRAY_BROWN",
"GREY_BROWN",
]
def color_to_rgb(color: Color | str) -> np.ndarray:
if isinstance(color, str):
return hex_to_rgb(color)
elif isinstance(color, Color):
return np.array(color.get_rgb())
else:
raise ValueError("Invalid color type: " + str(color))
def color_to_rgba(color: Color | str, alpha: float = 1) -> np.ndarray:
return np.array([*color_to_rgb(color), alpha])
def rgb_to_color(rgb: Iterable[float]) -> Color:
return Color(rgb=rgb)
def rgba_to_color(rgba: Iterable[float]) -> Color:
return rgb_to_color(rgba[:3])
def rgb_to_hex(rgb: Iterable[float]) -> str:
return "#" + "".join("%02x" % round(255 * x) for x in rgb)
def hex_to_rgb(hex_code: str) -> np.ndarray:
hex_part = hex_code[1:]
if len(hex_part) == 3:
hex_part = "".join([2 * c for c in hex_part])
return np.array([int(hex_part[i : i + 2], 16) / 255 for i in range(0, 6, 2)])
def invert_color(color: Color) -> Color:
return rgb_to_color(1.0 - color_to_rgb(color))
def color_to_int_rgb(color: Color) -> np.ndarray:
return (255 * color_to_rgb(color)).astype("uint8")
def color_to_int_rgba(color: Color, opacity: float = 1.0) -> np.ndarray:
alpha_multiplier = np.vectorize(lambda x: int(x * opacity))
return alpha_multiplier(np.append(color_to_int_rgb(color), 255))
def color_gradient(
reference_colors: Iterable[Color],
length_of_output: int,
) -> list[Color]:
if length_of_output == 0:
return reference_colors[0]
rgbs = list(map(color_to_rgb, reference_colors))
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
floors = alphas.astype("int")
alphas_mod1 = alphas % 1
# End edge case
alphas_mod1[-1] = 1
floors[-1] = len(rgbs) - 2
return [
rgb_to_color(interpolate(rgbs[i], rgbs[i + 1], alpha))
for i, alpha in zip(floors, alphas_mod1)
]
def interpolate_color(color1: Color, color2: Color, alpha: float) -> Color:
rgb = interpolate(color_to_rgb(color1), color_to_rgb(color2), alpha)
return rgb_to_color(rgb)
def average_color(*colors: Color) -> Color:
rgbs = np.array(list(map(color_to_rgb, colors)))
mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
return rgb_to_color(mean_rgb)
def random_bright_color() -> Color:
color = random_color()
curr_rgb = color_to_rgb(color)
new_rgb = interpolate(curr_rgb, np.ones(len(curr_rgb)), 0.5)
return Color(rgb=new_rgb)
def random_color() -> Color:
return random.choice([c.value for c in list(Colors)])
def get_shaded_rgb(
rgb: np.ndarray,
point: np.ndarray,
unit_normal_vect: np.ndarray,
light_source: np.ndarray,
) -> np.ndarray:
to_sun = normalize(light_source - point)
factor = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3
if factor < 0:
factor *= 0.5
result = rgb + factor
return result
COLORMAP_3B1B: list[Color] = [BLUE_E, GREEN, YELLOW, RED]
def get_colormap_list(map_name: str = "viridis", n_colors: int = 9) -> np.ndarray:
"""
Options for map_name:
3b1b_colormap
magma
inferno
plasma
viridis
cividis
twilight
twilight_shifted
turbo
"""
from matplotlib.cm import get_cmap
if map_name == "3b1b_colormap":
rgbs = np.array([color_to_rgb(color) for color in COLORMAP_3B1B])
else:
rgbs = get_cmap(map_name).colors # Make more general?
return resize_with_interpolation(np.array(rgbs), n_colors)

234
manim/utils/color/AS2700.py Normal file
View file

@ -0,0 +1,234 @@
"""Australian Color Standard
In 1985 the Australian Independent Color Standard AS 2700 was created. In
this standard, all colors can be identified via a category code (one of
B -- Blue, G -- Green, N -- Neutrals (grey), P -- Purple, R -- Red, T -- Blue/Green,
X -- Yellow/Red, Y -- Yellow) and a number. The colors also have (natural) names.
To use the colors from this list, access them directly from the module (which
is exposed to Manim's global name space):
.. code:: pycon
>>> from manim import AS2700
>>> AS2700.B23_BRIGHT_BLUE
ManimColor('#174F90')
List of Color Constants
-----------------------
These hex values (taken from https://www.w3schools.com/colors/colors_australia.asp)
are non official approximate values intended to simulate AS 2700 colors:
.. automanimcolormodule:: manim.utils.color.AS2700
"""
from .core import ManimColor
B11_RICH_BLUE = ManimColor("#2B3770")
B12_ROYAL_BLUE = ManimColor("#2C3563")
B13_NAVY_BLUE = ManimColor("#28304D")
B14_SAPHHIRE = ManimColor("#28426B")
B15_MID_BLUE = ManimColor("#144B6F")
B21_ULTRAMARINE = ManimColor("#2C5098")
B22_HOMEBUSH_BLUE = ManimColor("#215097")
B23_BRIGHT_BLUE = ManimColor("#174F90")
B24_HARBOUR_BLUE = ManimColor("#1C6293")
B25_AQUA = ManimColor("#5097AC")
B32_POWDER_BLUE = ManimColor("#B7C8DB")
B33_MIST_BLUE = ManimColor("#E0E6E2")
B34_PARADISE_BLUE = ManimColor("#3499BA")
B35_PALE_BLUE = ManimColor("#CDE4E2")
B41_BLUEBELL = ManimColor("#5B94D1")
B42_PURPLE_BLUE = ManimColor("#5E7899")
B43_GREY_BLUE = ManimColor("#627C8D")
B44_LIGHT_GREY_BLUE = ManimColor("#C0C0C1")
B45_SKY_BLUE = ManimColor("#7DB7C7")
B51_PERIWINKLE = ManimColor("#3871AC")
B53_DARK_GREY_BLUE = ManimColor("#4F6572")
B55_STORM_BLUE = ManimColor("#3F7C94")
B61_CORAL_SEA = ManimColor("#2B3873")
B62_MIDNIGHT_BLUE = ManimColor("#292A34")
B64_CHARCOAL = ManimColor("#363E45")
G11_BOTTLE_GREEN = ManimColor("#253A32")
G12_HOLLY = ManimColor("#21432D")
G13_EMERALD = ManimColor("#195F35")
G14_MOSS_GREEN = ManimColor("#33572D")
G15_RAINFOREST_GREEN = ManimColor("#3D492D")
G16_TRAFFIC_GREEN = ManimColor("#305442")
G17_MINT_GREEN = ManimColor("#006B45")
G21_JADE = ManimColor("#127453")
G22_SERPENTINE = ManimColor("#78A681")
G23_SHAMROCK = ManimColor("#336634")
G24_FERN_TREE = ManimColor("#477036")
G25_OLIVE = ManimColor("#595B2A")
G26_APPLE_GREEN = ManimColor("#4E9843")
G27_HOMEBUSH_GREEN = ManimColor("#017F4D")
G31_VERTIGRIS = ManimColor("#468A65")
G32_OPALINE = ManimColor("#AFCBB8")
G33_LETTUCE = ManimColor("#7B9954")
G34_AVOCADO = ManimColor("#757C4C")
G35_LIME_GREEN = ManimColor("#89922E")
G36_KIKUYU = ManimColor("#95B43B")
G37_BEANSTALK = ManimColor("#45A56A")
G41_LAWN_GREEN = ManimColor("#0D875D")
G42_GLACIER = ManimColor("#D5E1D2")
G43_SURF_GREEN = ManimColor("#C8C8A7")
G44_PALM_GREEN = ManimColor("#99B179")
G45_CHARTREUSE = ManimColor("#C7C98D")
G46_CITRONELLA = ManimColor("#BFC83E")
G47_CRYSTAL_GREEN = ManimColor("#ADCCA8")
G51_SPRUCE = ManimColor("#05674F")
G52_EUCALYPTUS = ManimColor("#66755B")
G53_BANKSIA = ManimColor("#929479")
G54_MIST_GREEN = ManimColor("#7A836D")
G55_LICHEN = ManimColor("#A7A98C")
G56_SAGE_GREEN = ManimColor("#677249")
G61_DARK_GREEN = ManimColor("#283533")
G62_RIVERGUM = ManimColor("#617061")
G63_DEEP_BRONZE_GREEN = ManimColor("#333334")
G64_SLATE = ManimColor("#5E6153")
G65_TI_TREE = ManimColor("#5D5F4E")
G66_ENVIRONMENT_GREEN = ManimColor("#484C3F")
G67_ZUCCHINI = ManimColor("#2E443A")
N11_PEARL_GREY = ManimColor("#D8D3C7")
N12_PASTEL_GREY = ManimColor("#CCCCCC")
N14_WHITE = ManimColor("#FFFFFF")
N15_HOMEBUSH_GREY = ManimColor("#A29B93")
N22_CLOUD_GREY = ManimColor("#C4C1B9")
N23_NEUTRAL_GREY = ManimColor("#CCCCCC")
N24_SILVER_GREY = ManimColor("#BDC7C5")
N25_BIRCH_GREY = ManimColor("#ABA498")
N32_GREEN_GREY = ManimColor("#8E9282")
N33_LIGHTBOX_GREY = ManimColor("#ACADAD")
N35_LIGHT_GREY = ManimColor("#A6A7A1")
N41_OYSTER = ManimColor("#998F78")
N42_STORM_GREY = ManimColor("#858F88")
N43_PIPELINE_GREY = ManimColor("#999999")
N44_BRIDGE_GREY = ManimColor("#767779")
N45_KOALA_GREY = ManimColor("#928F88")
N52_MID_GREY = ManimColor("#727A77")
N53_BLUE_GREY = ManimColor("#7C8588")
N54_BASALT = ManimColor("#585C63")
N55_LEAD_GREY = ManimColor("#5E5C58")
N61_BLACK = ManimColor("#2A2A2C")
N63_PEWTER = ManimColor("#596064")
N64_DARK_GREY = ManimColor("#4B5259")
N65_GRAPHITE_GREY = ManimColor("#45474A")
P11_MAGENTA = ManimColor("#7B2B48")
P12_PURPLE = ManimColor("#85467B")
P13_VIOLET = ManimColor("#5D3A61")
P14_BLUEBERRY = ManimColor("#4C4176")
P21_SUNSET_PINK = ManimColor("#E3BBBD")
P22_CYCLAMEN = ManimColor("#83597D")
P23_LILAC = ManimColor("#A69FB1")
P24_JACKARANDA = ManimColor("#795F91")
P31_DUSTY_PINK = ManimColor("#DBBEBC")
P33_RIBBON_PINK = ManimColor("#D1BCC9")
P41_ERICA_PINK = ManimColor("#C55A83")
P42_MULBERRY = ManimColor("#A06574")
P43_WISTERIA = ManimColor("#756D91")
P52_PLUM = ManimColor("#6E3D4B")
R11_INTERNATIONAL_ORANGE = ManimColor("#CE482A")
R12_SCARLET = ManimColor("#CD392A")
R13_SIGNAL_RED = ManimColor("#BA312B")
R14_WARATAH = ManimColor("#AA2429")
R15_CRIMSON = ManimColor("#9E2429")
R21_TANGERINE = ManimColor("#E96957")
R22_HOMEBUSH_RED = ManimColor("#D83A2D")
R23_LOLLIPOP = ManimColor("#CC5058")
R24_STRAWBERRY = ManimColor("#B4292A")
R25_ROSE_PINK = ManimColor("#E8919C")
R32_APPLE_BLOSSOM = ManimColor("#F2E1D8")
R33_GHOST_GUM = ManimColor("#E8DAD4")
R34_MUSHROOM = ManimColor("#D7C0B6")
R35_DEEP_ROSE = ManimColor("#CD6D71")
R41_SHELL_PINK = ManimColor("#F9D9BB")
R42_SALMON_PINK = ManimColor("#D99679")
R43_RED_DUST = ManimColor("#D0674F")
R44_POSSUM = ManimColor("#A18881")
R45_RUBY = ManimColor("#8F3E5C")
R51_BURNT_PINK = ManimColor("#E19B8E")
R52_TERRACOTTA = ManimColor("#A04C36")
R53_RED_GUM = ManimColor("#8D4338")
R54_RASPBERRY = ManimColor("#852F31")
R55_CLARET = ManimColor("#67292D")
R62_VENETIAN_RED = ManimColor("#77372B")
R63_RED_OXIDE = ManimColor("#663334")
R64_DEEP_INDIAN_RED = ManimColor("#542E2B")
R65_MAROON = ManimColor("#3F2B3C")
T11_TROPICAL_BLUE = ManimColor("#006698")
T12_DIAMANTIA = ManimColor("#006C74")
T14_MALACHITE = ManimColor("#105154")
T15_TURQUOISE = ManimColor("#098587")
T22_ORIENTAL_BLUE = ManimColor("#358792")
T24_BLUE_JADE = ManimColor("#427F7E")
T32_HUON_GREEN = ManimColor("#72B3B1")
T33_SMOKE_BLUE = ManimColor("#9EB6B2")
T35_GREEN_ICE = ManimColor("#78AEA2")
T44_BLUE_GUM = ManimColor("#6A8A88")
T45_COOTAMUNDRA = ManimColor("#759E91")
T51_MOUNTAIN_BLUE = ManimColor("#295668")
T53_PEACOCK_BLUE = ManimColor("#245764")
T63_TEAL = ManimColor("#183F4E")
X11_BUTTERSCOTCH = ManimColor("#D38F43")
X12_PUMPKIN = ManimColor("#DD7E1A")
X13_MARIGOLD = ManimColor("#ED7F15")
X14_MANDARIN = ManimColor("#E45427")
X15_ORANGE = ManimColor("#E36C2B")
X21_PALE_OCHRE = ManimColor("#DAA45F")
X22_SAFFRON = ManimColor("#F6AA51")
X23_APRICOT = ManimColor("#FEB56D")
X24_ROCKMELON = ManimColor("#F6894B")
X31_RAFFIA = ManimColor("#EBC695")
X32_MAGNOLIA = ManimColor("#F1DEBE")
X33_WARM_WHITE = ManimColor("#F3E7D4")
X34_DRIFTWOOD = ManimColor("#D5C4AE")
X41_BUFF = ManimColor("#C28A44")
X42_BISCUIT = ManimColor("#DEBA92")
X43_BEIGE = ManimColor("#C9AA8C")
X45_CINNAMON = ManimColor("#AC826D")
X51_TAN = ManimColor("#8F5F32")
X52_COFFEE = ManimColor("#AD7948")
X53_GOLDEN_TAN = ManimColor("#925629")
X54_BROWN = ManimColor("#68452C")
X55_NUT_BROWN = ManimColor("#764832")
X61_WOMBAT = ManimColor("#6E5D52")
X62_DARK_EARTH = ManimColor("#6E5D52")
X63_IRONBARK = ManimColor("#443B36")
X64_CHOCOLATE = ManimColor("#4A3B31")
X65_DARK_BROWN = ManimColor("#4F372D")
Y11_CANARY = ManimColor("#E7BD11")
Y12_WATTLE = ManimColor("#E8AF01")
Y13_VIVID_YELLOW = ManimColor("#FCAE01")
Y14_GOLDEN_YELLOW = ManimColor("#F5A601")
Y15_SUNFLOWER = ManimColor("#FFA709")
Y16_INCA_GOLD = ManimColor("#DF8C19")
Y21_PRIMROSE = ManimColor("#F5CF5B")
Y22_CUSTARD = ManimColor("#EFD25C")
Y23_BUTTERCUP = ManimColor("#E0CD41")
Y24_STRAW = ManimColor("#E3C882")
Y25_DEEP_CREAM = ManimColor("#F3C968")
Y26_HOMEBUSH_GOLD = ManimColor("#FCC51A")
Y31_LILY_GREEN = ManimColor("#E3E3CD")
Y32_FLUMMERY = ManimColor("#E6DF9E")
Y33_PALE_PRIMROSE = ManimColor("#F5F3CE")
Y34_CREAM = ManimColor("#EFE3BE")
Y35_OFF_WHITE = ManimColor("#F1E9D5")
Y41_OLIVE_YELLOW = ManimColor("#8E7426")
Y42_MUSTARD = ManimColor("#C4A32E")
Y43_PARCHMENT = ManimColor("#D4C9A3")
Y44_SAND = ManimColor("#DCC18B")
Y45_MANILLA = ManimColor("#E5D0A7")
Y51_BRONZE_OLIVE = ManimColor("#695D3E")
Y52_CHAMOIS = ManimColor("#BEA873")
Y53_SANDSTONE = ManimColor("#D5BF8E")
Y54_OATMEAL = ManimColor("#CAAE82")
Y55_DEEP_STONE = ManimColor("#BC9969")
Y56_MERINO = ManimColor("#C9B79E")
Y61_BLACK_OLIVE = ManimColor("#47473B")
Y62_SUGAR_CANE = ManimColor("#BCA55C")
Y63_KHAKI = ManimColor("#826843")
Y65_MUSHROOM = ManimColor("#A39281")
Y66_MUDSTONE = ManimColor("#574E45")

315
manim/utils/color/BS381.py Normal file
View file

@ -0,0 +1,315 @@
"""British Color Standard
This module contains colors defined in one of the British Standards
for colors, BS381C. This standard specifies colors used in identification,
coding, and other special purposes. See https://www.britishstandardcolour.com/
for more information.
To use the colors from this list, access them directly from the module (which
is exposed to Manim's global name space):
.. code:: pycon
>>> from manim import BS381
>>> BS381.OXFORD_BLUE
ManimColor('#1F3057')
List of Color Constants
-----------------------
These hex values (taken from https://www.w3schools.com/colors/colors_british.asp)
are non official approximate values intended to simulate the ones defined
in the standard:
.. automanimcolormodule:: manim.utils.color.BS381
"""
from .core import ManimColor
BS381_101 = ManimColor("#94BFAC")
SKY_BLUE = ManimColor("#94BFAC")
BS381_102 = ManimColor("#5B9291")
TURQUOISE_BLUE = ManimColor("#5B9291")
BS381_103 = ManimColor("#3B6879")
PEACOCK_BLUE = ManimColor("#3B6879")
BS381_104 = ManimColor("#264D7E")
AZURE_BLUE = ManimColor("#264D7E")
BS381_105 = ManimColor("#1F3057")
OXFORD_BLUE = ManimColor("#1F3057")
BS381_106 = ManimColor("#2A283D")
ROYAL_BLUE = ManimColor("#2A283D")
BS381_107 = ManimColor("#3A73A9")
STRONG_BLUE = ManimColor("#3A73A9")
BS381_108 = ManimColor("#173679")
AIRCRAFT_BLUE = ManimColor("#173679")
BS381_109 = ManimColor("#1C5680")
MIDDLE_BLUE = ManimColor("#1C5680")
BS381_110 = ManimColor("#2C3E75")
ROUNDEL_BLUE = ManimColor("#2C3E75")
BS381_111 = ManimColor("#8CC5BB")
PALE_BLUE = ManimColor("#8CC5BB")
BS381_112 = ManimColor("#78ADC2")
ARCTIC_BLUE = ManimColor("#78ADC2")
FIESTA_BLUE = ManimColor("#78ADC2")
BS381_113 = ManimColor("#3F687D")
DEEP_SAXE_BLUE = ManimColor("#3F687D")
BS381_114 = ManimColor("#1F4B61")
RAIL_BLUE = ManimColor("#1F4B61")
BS381_115 = ManimColor("#5F88C1")
COBALT_BLUE = ManimColor("#5F88C1")
BS381_166 = ManimColor("#2458AF")
FRENCH_BLUE = ManimColor("#2458AF")
BS381_169 = ManimColor("#135B75")
TRAFFIC_BLUE = ManimColor("#135B75")
BS381_172 = ManimColor("#A7C6EB")
PALE_ROUNDEL_BLUE = ManimColor("#A7C6EB")
BS381_174 = ManimColor("#64A0AA")
ORIENT_BLUE = ManimColor("#64A0AA")
BS381_175 = ManimColor("#4F81C5")
LIGHT_FRENCH_BLUE = ManimColor("#4F81C5")
BS381_210 = ManimColor("#BBC9A5")
SKY = ManimColor("#BBC9A5")
BS381_216 = ManimColor("#BCD890")
EAU_DE_NIL = ManimColor("#BCD890")
BS381_217 = ManimColor("#96BF65")
SEA_GREEN = ManimColor("#96BF65")
BS381_218 = ManimColor("#698B47")
GRASS_GREEN = ManimColor("#698B47")
BS381_219 = ManimColor("#757639")
SAGE_GREEN = ManimColor("#757639")
BS381_220 = ManimColor("#4B5729")
OLIVE_GREEN = ManimColor("#4B5729")
BS381_221 = ManimColor("#507D3A")
BRILLIANT_GREEN = ManimColor("#507D3A")
BS381_222 = ManimColor("#6A7031")
LIGHT_BRONZE_GREEN = ManimColor("#6A7031")
BS381_223 = ManimColor("#49523A")
MIDDLE_BRONZE_GREEN = ManimColor("#49523A")
BS381_224 = ManimColor("#3E4630")
DEEP_BRONZE_GREEN = ManimColor("#3E4630")
BS381_225 = ManimColor("#406A28")
LIGHT_BRUNSWICK_GREEN = ManimColor("#406A28")
BS381_226 = ManimColor("#33533B")
MID_BRUNSWICK_GREEN = ManimColor("#33533B")
BS381_227 = ManimColor("#254432")
DEEP_BRUNSWICK_GREEN = ManimColor("#254432")
BS381_228 = ManimColor("#428B64")
EMERALD_GREEN = ManimColor("#428B64")
BS381_241 = ManimColor("#4F5241")
DARK_GREEN = ManimColor("#4F5241")
BS381_262 = ManimColor("#44945E")
BOLD_GREEN = ManimColor("#44945E")
BS381_267 = ManimColor("#476A4C")
DEEP_CHROME_GREEN = ManimColor("#476A4C")
TRAFFIC_GREEN = ManimColor("#476A4C")
BS381_275 = ManimColor("#8FC693")
OPALINE_GREEN = ManimColor("#8FC693")
BS381_276 = ManimColor("#2E4C1E")
LINCON_GREEN = ManimColor("#2E4C1E")
BS381_277 = ManimColor("#364A20")
CYPRESS_GREEN = ManimColor("#364A20")
BS381_278 = ManimColor("#87965A")
LIGHT_OLIVE_GREEN = ManimColor("#87965A")
BS381_279 = ManimColor("#3B3629")
STEEL_FURNITURE_GREEN = ManimColor("#3B3629")
BS381_280 = ManimColor("#68AB77")
VERDIGRIS_GREEN = ManimColor("#68AB77")
BS381_282 = ManimColor("#506B52")
FOREST_GREEN = ManimColor("#506B52")
BS381_283 = ManimColor("#7E8F6E")
AIRCRAFT_GREY_GREEN = ManimColor("#7E8F6E")
BS381_284 = ManimColor("#6B6F5A")
SPRUCE_GREEN = ManimColor("#6B6F5A")
BS381_285 = ManimColor("#5F5C4B")
NATO_GREEN = ManimColor("#5F5C4B")
BS381_298 = ManimColor("#4F5138")
OLIVE_DRAB = ManimColor("#4F5138")
BS381_309 = ManimColor("#FEEC04")
CANARY_YELLOW = ManimColor("#FEEC04")
BS381_310 = ManimColor("#FEF963")
PRIMROSE = ManimColor("#FEF963")
BS381_315 = ManimColor("#FEF96A")
GRAPEFRUIT = ManimColor("#FEF96A")
BS381_320 = ManimColor("#9E7339")
LIGHT_BROWN = ManimColor("#9E7339")
BS381_337 = ManimColor("#4C4A3C")
VERY_DARK_DRAB = ManimColor("#4C4A3C")
BS381_350 = ManimColor("#7B6B4F")
DARK_EARTH = ManimColor("#7B6B4F")
BS381_352 = ManimColor("#FCED96")
PALE_CREAM = ManimColor("#FCED96")
BS381_353 = ManimColor("#FDF07A")
DEEP_CREAM = ManimColor("#FDF07A")
BS381_354 = ManimColor("#E9BB43")
PRIMROSE_2 = ManimColor("#E9BB43")
BS381_355 = ManimColor("#FDD906")
LEMON = ManimColor("#FDD906")
BS381_356 = ManimColor("#FCC808")
GOLDEN_YELLOW = ManimColor("#FCC808")
BS381_358 = ManimColor("#F6C870")
LIGHT_BUFF = ManimColor("#F6C870")
BS381_359 = ManimColor("#DBAC50")
MIDDLE_BUFF = ManimColor("#DBAC50")
BS381_361 = ManimColor("#D4B97D")
LIGHT_STONE = ManimColor("#D4B97D")
BS381_362 = ManimColor("#AC7C42")
MIDDLE_STONE = ManimColor("#AC7C42")
BS381_363 = ManimColor("#FDE706")
BOLD_YELLOW = ManimColor("#FDE706")
BS381_364 = ManimColor("#CEC093")
PORTLAND_STONE = ManimColor("#CEC093")
BS381_365 = ManimColor("#F4F0BD")
VELLUM = ManimColor("#F4F0BD")
BS381_366 = ManimColor("#F5E7A1")
LIGHT_BEIGE = ManimColor("#F5E7A1")
BS381_367 = ManimColor("#FEF6BF")
MANILLA = ManimColor("#fef6bf")
BS381_368 = ManimColor("#DD7B00")
TRAFFIC_YELLOW = ManimColor("#DD7B00")
BS381_369 = ManimColor("#FEEBA8")
BISCUIT = ManimColor("#feeba8")
BS381_380 = ManimColor("#BBA38A")
CAMOUFLAGE_DESERT_SAND = ManimColor("#BBA38A")
BS381_384 = ManimColor("#EEDFA5")
LIGHT_STRAW = ManimColor("#EEDFA5")
BS381_385 = ManimColor("#E8C88F")
LIGHT_BISCUIT = ManimColor("#E8C88F")
BS381_386 = ManimColor("#E6C18D")
CHAMPAGNE = ManimColor("#e6c18d")
BS381_387 = ManimColor("#CFB48A")
SUNRISE = ManimColor("#cfb48a")
SUNSHINE = ManimColor("#cfb48a")
BS381_388 = ManimColor("#E4CF93")
BEIGE = ManimColor("#e4cf93")
BS381_389 = ManimColor("#B2A788")
CAMOUFLAGE_BEIGE = ManimColor("#B2A788")
BS381_397 = ManimColor("#F3D163")
JASMINE_YELLOW = ManimColor("#F3D163")
BS381_411 = ManimColor("#74542F")
MIDDLE_BROWN = ManimColor("#74542F")
BS381_412 = ManimColor("#5C422E")
DARK_BROWN = ManimColor("#5C422E")
BS381_413 = ManimColor("#402D21")
NUT_BROWN = ManimColor("#402D21")
BS381_414 = ManimColor("#A86C29")
GOLDEN_BROWN = ManimColor("#A86C29")
BS381_415 = ManimColor("#61361E")
IMPERIAL_BROWN = ManimColor("#61361E")
BS381_420 = ManimColor("#A89177")
DARK_CAMOUFLAGE_DESERT_SAND = ManimColor("#A89177")
BS381_435 = ManimColor("#845B4D")
CAMOUFLAGE_RED = ManimColor("#845B4D")
BS381_436 = ManimColor("#564B47")
DARK_CAMOUFLAGE_BROWN = ManimColor("#564B47")
BS381_439 = ManimColor("#753B1E")
ORANGE_BROWN = ManimColor("#753B1E")
BS381_443 = ManimColor("#C98A71")
SALMON = ManimColor("#c98a71")
BS381_444 = ManimColor("#A65341")
TERRACOTTA = ManimColor("#a65341")
BS381_445 = ManimColor("#83422B")
VENETIAN_RED = ManimColor("#83422B")
BS381_446 = ManimColor("#774430")
RED_OXIDE = ManimColor("#774430")
BS381_447 = ManimColor("#F3B28B")
SALMON_PINK = ManimColor("#F3B28B")
BS381_448 = ManimColor("#67403A")
DEEP_INDIAN_RED = ManimColor("#67403A")
BS381_449 = ManimColor("#693B3F")
LIGHT_PURPLE_BROWN = ManimColor("#693B3F")
BS381_452 = ManimColor("#613339")
DARK_CRIMSON = ManimColor("#613339")
BS381_453 = ManimColor("#FBDED6")
SHELL_PINK = ManimColor("#FBDED6")
BS381_454 = ManimColor("#E8A1A2")
PALE_ROUNDEL_RED = ManimColor("#E8A1A2")
BS381_460 = ManimColor("#BD8F56")
DEEP_BUFF = ManimColor("#BD8F56")
BS381_473 = ManimColor("#793932")
GULF_RED = ManimColor("#793932")
BS381_489 = ManimColor("#8D5B41")
LEAF_BROWN = ManimColor("#8D5B41")
BS381_490 = ManimColor("#573320")
BEECH_BROWN = ManimColor("#573320")
BS381_499 = ManimColor("#59493E")
SERVICE_BROWN = ManimColor("#59493E")
BS381_536 = ManimColor("#BB3016")
POPPY = ManimColor("#bb3016")
BS381_537 = ManimColor("#DD3420")
SIGNAL_RED = ManimColor("#DD3420")
BS381_538 = ManimColor("#C41C22")
POST_OFFICE_RED = ManimColor("#C41C22")
CHERRY = ManimColor("#c41c22")
BS381_539 = ManimColor("#D21E2B")
CURRANT_RED = ManimColor("#D21E2B")
BS381_540 = ManimColor("#8B1A32")
CRIMSON = ManimColor("#8b1a32")
BS381_541 = ManimColor("#471B21")
MAROON = ManimColor("#471b21")
BS381_542 = ManimColor("#982D57")
RUBY = ManimColor("#982d57")
BS381_557 = ManimColor("#EF841E")
LIGHT_ORANGE = ManimColor("#EF841E")
BS381_564 = ManimColor("#DD3524")
BOLD_RED = ManimColor("#DD3524")
BS381_568 = ManimColor("#FB9C06")
APRICOT = ManimColor("#fb9c06")
BS381_570 = ManimColor("#A83C19")
TRAFFIC_RED = ManimColor("#A83C19")
BS381_591 = ManimColor("#D04E09")
DEEP_ORANGE = ManimColor("#D04E09")
BS381_592 = ManimColor("#E45523")
INTERNATIONAL_ORANGE = ManimColor("#E45523")
BS381_593 = ManimColor("#F24816")
RAIL_RED = ManimColor("#F24816")
AZO_ORANGE = ManimColor("#F24816")
BS381_626 = ManimColor("#A0A9AA")
CAMOUFLAGE_GREY = ManimColor("#A0A9AA")
BS381_627 = ManimColor("#BEC0B8")
LIGHT_AIRCRAFT_GREY = ManimColor("#BEC0B8")
BS381_628 = ManimColor("#9D9D7E")
SILVER_GREY = ManimColor("#9D9D7E")
BS381_629 = ManimColor("#7A838B")
DARK_CAMOUFLAGE_GREY = ManimColor("#7A838B")
BS381_630 = ManimColor("#A5AD98")
FRENCH_GREY = ManimColor("#A5AD98")
BS381_631 = ManimColor("#9AAA9F")
LIGHT_GREY = ManimColor("#9AAA9F")
BS381_632 = ManimColor("#6B7477")
DARK_ADMIRALTY_GREY = ManimColor("#6B7477")
BS381_633 = ManimColor("#424C53")
RAF_BLUE_GREY = ManimColor("#424C53")
BS381_634 = ManimColor("#6F7264")
SLATE = ManimColor("#6f7264")
BS381_635 = ManimColor("#525B55")
LEAD = ManimColor("#525b55")
BS381_636 = ManimColor("#5F7682")
PRU_BLUE = ManimColor("#5F7682")
BS381_637 = ManimColor("#8E9B9C")
MEDIUM_SEA_GREY = ManimColor("#8E9B9C")
BS381_638 = ManimColor("#6C7377")
DARK_SEA_GREY = ManimColor("#6C7377")
BS381_639 = ManimColor("#667563")
LIGHT_SLATE_GREY = ManimColor("#667563")
BS381_640 = ManimColor("#566164")
EXTRA_DARK_SEA_GREY = ManimColor("#566164")
BS381_642 = ManimColor("#282B2F")
NIGHT = ManimColor("#282b2f")
BS381_671 = ManimColor("#4E5355")
MIDDLE_GRAPHITE = ManimColor("#4E5355")
BS381_676 = ManimColor("#A9B7B9")
LIGHT_WEATHERWORK_GREY = ManimColor("#A9B7B9")
BS381_677 = ManimColor("#676F76")
DARK_WEATHERWORK_GREY = ManimColor("#676F76")
BS381_692 = ManimColor("#7B93A3")
SMOKE_GREY = ManimColor("#7B93A3")
BS381_693 = ManimColor("#88918D")
AIRCRAFT_GREY = ManimColor("#88918D")
BS381_694 = ManimColor("#909A92")
DOVE_GREY = ManimColor("#909A92")
BS381_697 = ManimColor("#B6D3CC")
LIGHT_ADMIRALTY_GREY = ManimColor("#B6D3CC")
BS381_796 = ManimColor("#6E4A75")
DARK_VIOLET = ManimColor("#6E4A75")
BS381_797 = ManimColor("#C9A8CE")
LIGHT_VIOLET = ManimColor("#C9A8CE")

530
manim/utils/color/X11.py Normal file
View file

@ -0,0 +1,530 @@
# from https://www.w3schools.com/colors/colors_x11.asp
"""X11 Colors
These color and their names (taken from
https://www.w3schools.com/colors/colors_x11.asp) were developed at the
Massachusetts Intitute of Technology (MIT) during
the development of color based computer display system.
To use the colors from this list, access them directly from the module (which
is exposed to Manim's global name space):
.. code:: pycon
>>> from manim import X11
>>> X11.BEIGE
ManimColor('#F5F5DC')
List of Color Constants
-----------------------
.. automanimcolormodule:: manim.utils.color.X11
"""
from .core import ManimColor
ALICEBLUE = ManimColor("#F0F8FF")
ANTIQUEWHITE = ManimColor("#FAEBD7")
ANTIQUEWHITE1 = ManimColor("#FFEFDB")
ANTIQUEWHITE2 = ManimColor("#EEDFCC")
ANTIQUEWHITE3 = ManimColor("#CDC0B0")
ANTIQUEWHITE4 = ManimColor("#8B8378")
AQUAMARINE1 = ManimColor("#7FFFD4")
AQUAMARINE2 = ManimColor("#76EEC6")
AQUAMARINE4 = ManimColor("#458B74")
AZURE1 = ManimColor("#F0FFFF")
AZURE2 = ManimColor("#E0EEEE")
AZURE3 = ManimColor("#C1CDCD")
AZURE4 = ManimColor("#838B8B")
BEIGE = ManimColor("#F5F5DC")
BISQUE1 = ManimColor("#FFE4C4")
BISQUE2 = ManimColor("#EED5B7")
BISQUE3 = ManimColor("#CDB79E")
BISQUE4 = ManimColor("#8B7D6B")
BLACK = ManimColor("#000000")
BLANCHEDALMOND = ManimColor("#FFEBCD")
BLUE1 = ManimColor("#0000FF")
BLUE2 = ManimColor("#0000EE")
BLUE4 = ManimColor("#00008B")
BLUEVIOLET = ManimColor("#8A2BE2")
BROWN = ManimColor("#A52A2A")
BROWN1 = ManimColor("#FF4040")
BROWN2 = ManimColor("#EE3B3B")
BROWN3 = ManimColor("#CD3333")
BROWN4 = ManimColor("#8B2323")
BURLYWOOD = ManimColor("#DEB887")
BURLYWOOD1 = ManimColor("#FFD39B")
BURLYWOOD2 = ManimColor("#EEC591")
BURLYWOOD3 = ManimColor("#CDAA7D")
BURLYWOOD4 = ManimColor("#8B7355")
CADETBLUE = ManimColor("#5F9EA0")
CADETBLUE1 = ManimColor("#98F5FF")
CADETBLUE2 = ManimColor("#8EE5EE")
CADETBLUE3 = ManimColor("#7AC5CD")
CADETBLUE4 = ManimColor("#53868B")
CHARTREUSE1 = ManimColor("#7FFF00")
CHARTREUSE2 = ManimColor("#76EE00")
CHARTREUSE3 = ManimColor("#66CD00")
CHARTREUSE4 = ManimColor("#458B00")
CHOCOLATE = ManimColor("#D2691E")
CHOCOLATE1 = ManimColor("#FF7F24")
CHOCOLATE2 = ManimColor("#EE7621")
CHOCOLATE3 = ManimColor("#CD661D")
CORAL = ManimColor("#FF7F50")
CORAL1 = ManimColor("#FF7256")
CORAL2 = ManimColor("#EE6A50")
CORAL3 = ManimColor("#CD5B45")
CORAL4 = ManimColor("#8B3E2F")
CORNFLOWERBLUE = ManimColor("#6495ED")
CORNSILK1 = ManimColor("#FFF8DC")
CORNSILK2 = ManimColor("#EEE8CD")
CORNSILK3 = ManimColor("#CDC8B1")
CORNSILK4 = ManimColor("#8B8878")
CYAN1 = ManimColor("#00FFFF")
CYAN2 = ManimColor("#00EEEE")
CYAN3 = ManimColor("#00CDCD")
CYAN4 = ManimColor("#008B8B")
DARKGOLDENROD = ManimColor("#B8860B")
DARKGOLDENROD1 = ManimColor("#FFB90F")
DARKGOLDENROD2 = ManimColor("#EEAD0E")
DARKGOLDENROD3 = ManimColor("#CD950C")
DARKGOLDENROD4 = ManimColor("#8B6508")
DARKGREEN = ManimColor("#006400")
DARKKHAKI = ManimColor("#BDB76B")
DARKOLIVEGREEN = ManimColor("#556B2F")
DARKOLIVEGREEN1 = ManimColor("#CAFF70")
DARKOLIVEGREEN2 = ManimColor("#BCEE68")
DARKOLIVEGREEN3 = ManimColor("#A2CD5A")
DARKOLIVEGREEN4 = ManimColor("#6E8B3D")
DARKORANGE = ManimColor("#FF8C00")
DARKORANGE1 = ManimColor("#FF7F00")
DARKORANGE2 = ManimColor("#EE7600")
DARKORANGE3 = ManimColor("#CD6600")
DARKORANGE4 = ManimColor("#8B4500")
DARKORCHID = ManimColor("#9932CC")
DARKORCHID1 = ManimColor("#BF3EFF")
DARKORCHID2 = ManimColor("#B23AEE")
DARKORCHID3 = ManimColor("#9A32CD")
DARKORCHID4 = ManimColor("#68228B")
DARKSALMON = ManimColor("#E9967A")
DARKSEAGREEN = ManimColor("#8FBC8F")
DARKSEAGREEN1 = ManimColor("#C1FFC1")
DARKSEAGREEN2 = ManimColor("#B4EEB4")
DARKSEAGREEN3 = ManimColor("#9BCD9B")
DARKSEAGREEN4 = ManimColor("#698B69")
DARKSLATEBLUE = ManimColor("#483D8B")
DARKSLATEGRAY = ManimColor("#2F4F4F")
DARKSLATEGRAY1 = ManimColor("#97FFFF")
DARKSLATEGRAY2 = ManimColor("#8DEEEE")
DARKSLATEGRAY3 = ManimColor("#79CDCD")
DARKSLATEGRAY4 = ManimColor("#528B8B")
DARKTURQUOISE = ManimColor("#00CED1")
DARKVIOLET = ManimColor("#9400D3")
DEEPPINK1 = ManimColor("#FF1493")
DEEPPINK2 = ManimColor("#EE1289")
DEEPPINK3 = ManimColor("#CD1076")
DEEPPINK4 = ManimColor("#8B0A50")
DEEPSKYBLUE1 = ManimColor("#00BFFF")
DEEPSKYBLUE2 = ManimColor("#00B2EE")
DEEPSKYBLUE3 = ManimColor("#009ACD")
DEEPSKYBLUE4 = ManimColor("#00688B")
DIMGRAY = ManimColor("#696969")
DODGERBLUE1 = ManimColor("#1E90FF")
DODGERBLUE2 = ManimColor("#1C86EE")
DODGERBLUE3 = ManimColor("#1874CD")
DODGERBLUE4 = ManimColor("#104E8B")
FIREBRICK = ManimColor("#B22222")
FIREBRICK1 = ManimColor("#FF3030")
FIREBRICK2 = ManimColor("#EE2C2C")
FIREBRICK3 = ManimColor("#CD2626")
FIREBRICK4 = ManimColor("#8B1A1A")
FLORALWHITE = ManimColor("#FFFAF0")
FORESTGREEN = ManimColor("#228B22")
GAINSBORO = ManimColor("#DCDCDC")
GHOSTWHITE = ManimColor("#F8F8FF")
GOLD1 = ManimColor("#FFD700")
GOLD2 = ManimColor("#EEC900")
GOLD3 = ManimColor("#CDAD00")
GOLD4 = ManimColor("#8B7500")
GOLDENROD = ManimColor("#DAA520")
GOLDENROD1 = ManimColor("#FFC125")
GOLDENROD2 = ManimColor("#EEB422")
GOLDENROD3 = ManimColor("#CD9B1D")
GOLDENROD4 = ManimColor("#8B6914")
GRAY = ManimColor("#BEBEBE")
GRAY1 = ManimColor("#030303")
GRAY2 = ManimColor("#050505")
GRAY3 = ManimColor("#080808")
GRAY4 = ManimColor("#0A0A0A")
GRAY5 = ManimColor("#0D0D0D")
GRAY6 = ManimColor("#0F0F0F")
GRAY7 = ManimColor("#121212")
GRAY8 = ManimColor("#141414")
GRAY9 = ManimColor("#171717")
GRAY10 = ManimColor("#1A1A1A")
GRAY11 = ManimColor("#1C1C1C")
GRAY12 = ManimColor("#1F1F1F")
GRAY13 = ManimColor("#212121")
GRAY14 = ManimColor("#242424")
GRAY15 = ManimColor("#262626")
GRAY16 = ManimColor("#292929")
GRAY17 = ManimColor("#2B2B2B")
GRAY18 = ManimColor("#2E2E2E")
GRAY19 = ManimColor("#303030")
GRAY20 = ManimColor("#333333")
GRAY21 = ManimColor("#363636")
GRAY22 = ManimColor("#383838")
GRAY23 = ManimColor("#3B3B3B")
GRAY24 = ManimColor("#3D3D3D")
GRAY25 = ManimColor("#404040")
GRAY26 = ManimColor("#424242")
GRAY27 = ManimColor("#454545")
GRAY28 = ManimColor("#474747")
GRAY29 = ManimColor("#4A4A4A")
GRAY30 = ManimColor("#4D4D4D")
GRAY31 = ManimColor("#4F4F4F")
GRAY32 = ManimColor("#525252")
GRAY33 = ManimColor("#545454")
GRAY34 = ManimColor("#575757")
GRAY35 = ManimColor("#595959")
GRAY36 = ManimColor("#5C5C5C")
GRAY37 = ManimColor("#5E5E5E")
GRAY38 = ManimColor("#616161")
GRAY39 = ManimColor("#636363")
GRAY40 = ManimColor("#666666")
GRAY41 = ManimColor("#696969")
GRAY42 = ManimColor("#6B6B6B")
GRAY43 = ManimColor("#6E6E6E")
GRAY44 = ManimColor("#707070")
GRAY45 = ManimColor("#737373")
GRAY46 = ManimColor("#757575")
GRAY47 = ManimColor("#787878")
GRAY48 = ManimColor("#7A7A7A")
GRAY49 = ManimColor("#7D7D7D")
GRAY50 = ManimColor("#7F7F7F")
GRAY51 = ManimColor("#828282")
GRAY52 = ManimColor("#858585")
GRAY53 = ManimColor("#878787")
GRAY54 = ManimColor("#8A8A8A")
GRAY55 = ManimColor("#8C8C8C")
GRAY56 = ManimColor("#8F8F8F")
GRAY57 = ManimColor("#919191")
GRAY58 = ManimColor("#949494")
GRAY59 = ManimColor("#969696")
GRAY60 = ManimColor("#999999")
GRAY61 = ManimColor("#9C9C9C")
GRAY62 = ManimColor("#9E9E9E")
GRAY63 = ManimColor("#A1A1A1")
GRAY64 = ManimColor("#A3A3A3")
GRAY65 = ManimColor("#A6A6A6")
GRAY66 = ManimColor("#A8A8A8")
GRAY67 = ManimColor("#ABABAB")
GRAY68 = ManimColor("#ADADAD")
GRAY69 = ManimColor("#B0B0B0")
GRAY70 = ManimColor("#B3B3B3")
GRAY71 = ManimColor("#B5B5B5")
GRAY72 = ManimColor("#B8B8B8")
GRAY73 = ManimColor("#BABABA")
GRAY74 = ManimColor("#BDBDBD")
GRAY75 = ManimColor("#BFBFBF")
GRAY76 = ManimColor("#C2C2C2")
GRAY77 = ManimColor("#C4C4C4")
GRAY78 = ManimColor("#C7C7C7")
GRAY79 = ManimColor("#C9C9C9")
GRAY80 = ManimColor("#CCCCCC")
GRAY81 = ManimColor("#CFCFCF")
GRAY82 = ManimColor("#D1D1D1")
GRAY83 = ManimColor("#D4D4D4")
GRAY84 = ManimColor("#D6D6D6")
GRAY85 = ManimColor("#D9D9D9")
GRAY86 = ManimColor("#DBDBDB")
GRAY87 = ManimColor("#DEDEDE")
GRAY88 = ManimColor("#E0E0E0")
GRAY89 = ManimColor("#E3E3E3")
GRAY90 = ManimColor("#E5E5E5")
GRAY91 = ManimColor("#E8E8E8")
GRAY92 = ManimColor("#EBEBEB")
GRAY93 = ManimColor("#EDEDED")
GRAY94 = ManimColor("#F0F0F0")
GRAY95 = ManimColor("#F2F2F2")
GRAY97 = ManimColor("#F7F7F7")
GRAY98 = ManimColor("#FAFAFA")
GRAY99 = ManimColor("#FCFCFC")
GREEN1 = ManimColor("#00FF00")
GREEN2 = ManimColor("#00EE00")
GREEN3 = ManimColor("#00CD00")
GREEN4 = ManimColor("#008B00")
GREENYELLOW = ManimColor("#ADFF2F")
HONEYDEW1 = ManimColor("#F0FFF0")
HONEYDEW2 = ManimColor("#E0EEE0")
HONEYDEW3 = ManimColor("#C1CDC1")
HONEYDEW4 = ManimColor("#838B83")
HOTPINK = ManimColor("#FF69B4")
HOTPINK1 = ManimColor("#FF6EB4")
HOTPINK2 = ManimColor("#EE6AA7")
HOTPINK3 = ManimColor("#CD6090")
HOTPINK4 = ManimColor("#8B3A62")
INDIANRED = ManimColor("#CD5C5C")
INDIANRED1 = ManimColor("#FF6A6A")
INDIANRED2 = ManimColor("#EE6363")
INDIANRED3 = ManimColor("#CD5555")
INDIANRED4 = ManimColor("#8B3A3A")
IVORY1 = ManimColor("#FFFFF0")
IVORY2 = ManimColor("#EEEEE0")
IVORY3 = ManimColor("#CDCDC1")
IVORY4 = ManimColor("#8B8B83")
KHAKI = ManimColor("#F0E68C")
KHAKI1 = ManimColor("#FFF68F")
KHAKI2 = ManimColor("#EEE685")
KHAKI3 = ManimColor("#CDC673")
KHAKI4 = ManimColor("#8B864E")
LAVENDER = ManimColor("#E6E6FA")
LAVENDERBLUSH1 = ManimColor("#FFF0F5")
LAVENDERBLUSH2 = ManimColor("#EEE0E5")
LAVENDERBLUSH3 = ManimColor("#CDC1C5")
LAVENDERBLUSH4 = ManimColor("#8B8386")
LAWNGREEN = ManimColor("#7CFC00")
LEMONCHIFFON1 = ManimColor("#FFFACD")
LEMONCHIFFON2 = ManimColor("#EEE9BF")
LEMONCHIFFON3 = ManimColor("#CDC9A5")
LEMONCHIFFON4 = ManimColor("#8B8970")
LIGHT = ManimColor("#EEDD82")
LIGHTBLUE = ManimColor("#ADD8E6")
LIGHTBLUE1 = ManimColor("#BFEFFF")
LIGHTBLUE2 = ManimColor("#B2DFEE")
LIGHTBLUE3 = ManimColor("#9AC0CD")
LIGHTBLUE4 = ManimColor("#68838B")
LIGHTCORAL = ManimColor("#F08080")
LIGHTCYAN1 = ManimColor("#E0FFFF")
LIGHTCYAN2 = ManimColor("#D1EEEE")
LIGHTCYAN3 = ManimColor("#B4CDCD")
LIGHTCYAN4 = ManimColor("#7A8B8B")
LIGHTGOLDENROD1 = ManimColor("#FFEC8B")
LIGHTGOLDENROD2 = ManimColor("#EEDC82")
LIGHTGOLDENROD3 = ManimColor("#CDBE70")
LIGHTGOLDENROD4 = ManimColor("#8B814C")
LIGHTGOLDENRODYELLOW = ManimColor("#FAFAD2")
LIGHTGRAY = ManimColor("#D3D3D3")
LIGHTPINK = ManimColor("#FFB6C1")
LIGHTPINK1 = ManimColor("#FFAEB9")
LIGHTPINK2 = ManimColor("#EEA2AD")
LIGHTPINK3 = ManimColor("#CD8C95")
LIGHTPINK4 = ManimColor("#8B5F65")
LIGHTSALMON1 = ManimColor("#FFA07A")
LIGHTSALMON2 = ManimColor("#EE9572")
LIGHTSALMON3 = ManimColor("#CD8162")
LIGHTSALMON4 = ManimColor("#8B5742")
LIGHTSEAGREEN = ManimColor("#20B2AA")
LIGHTSKYBLUE = ManimColor("#87CEFA")
LIGHTSKYBLUE1 = ManimColor("#B0E2FF")
LIGHTSKYBLUE2 = ManimColor("#A4D3EE")
LIGHTSKYBLUE3 = ManimColor("#8DB6CD")
LIGHTSKYBLUE4 = ManimColor("#607B8B")
LIGHTSLATEBLUE = ManimColor("#8470FF")
LIGHTSLATEGRAY = ManimColor("#778899")
LIGHTSTEELBLUE = ManimColor("#B0C4DE")
LIGHTSTEELBLUE1 = ManimColor("#CAE1FF")
LIGHTSTEELBLUE2 = ManimColor("#BCD2EE")
LIGHTSTEELBLUE3 = ManimColor("#A2B5CD")
LIGHTSTEELBLUE4 = ManimColor("#6E7B8B")
LIGHTYELLOW1 = ManimColor("#FFFFE0")
LIGHTYELLOW2 = ManimColor("#EEEED1")
LIGHTYELLOW3 = ManimColor("#CDCDB4")
LIGHTYELLOW4 = ManimColor("#8B8B7A")
LIMEGREEN = ManimColor("#32CD32")
LINEN = ManimColor("#FAF0E6")
MAGENTA = ManimColor("#FF00FF")
MAGENTA2 = ManimColor("#EE00EE")
MAGENTA3 = ManimColor("#CD00CD")
MAGENTA4 = ManimColor("#8B008B")
MAROON = ManimColor("#B03060")
MAROON1 = ManimColor("#FF34B3")
MAROON2 = ManimColor("#EE30A7")
MAROON3 = ManimColor("#CD2990")
MAROON4 = ManimColor("#8B1C62")
MEDIUM = ManimColor("#66CDAA")
MEDIUMAQUAMARINE = ManimColor("#66CDAA")
MEDIUMBLUE = ManimColor("#0000CD")
MEDIUMORCHID = ManimColor("#BA55D3")
MEDIUMORCHID1 = ManimColor("#E066FF")
MEDIUMORCHID2 = ManimColor("#D15FEE")
MEDIUMORCHID3 = ManimColor("#B452CD")
MEDIUMORCHID4 = ManimColor("#7A378B")
MEDIUMPURPLE = ManimColor("#9370DB")
MEDIUMPURPLE1 = ManimColor("#AB82FF")
MEDIUMPURPLE2 = ManimColor("#9F79EE")
MEDIUMPURPLE3 = ManimColor("#8968CD")
MEDIUMPURPLE4 = ManimColor("#5D478B")
MEDIUMSEAGREEN = ManimColor("#3CB371")
MEDIUMSLATEBLUE = ManimColor("#7B68EE")
MEDIUMSPRINGGREEN = ManimColor("#00FA9A")
MEDIUMTURQUOISE = ManimColor("#48D1CC")
MEDIUMVIOLETRED = ManimColor("#C71585")
MIDNIGHTBLUE = ManimColor("#191970")
MINTCREAM = ManimColor("#F5FFFA")
MISTYROSE1 = ManimColor("#FFE4E1")
MISTYROSE2 = ManimColor("#EED5D2")
MISTYROSE3 = ManimColor("#CDB7B5")
MISTYROSE4 = ManimColor("#8B7D7B")
MOCCASIN = ManimColor("#FFE4B5")
NAVAJOWHITE1 = ManimColor("#FFDEAD")
NAVAJOWHITE2 = ManimColor("#EECFA1")
NAVAJOWHITE3 = ManimColor("#CDB38B")
NAVAJOWHITE4 = ManimColor("#8B795E")
NAVYBLUE = ManimColor("#000080")
OLDLACE = ManimColor("#FDF5E6")
OLIVEDRAB = ManimColor("#6B8E23")
OLIVEDRAB1 = ManimColor("#C0FF3E")
OLIVEDRAB2 = ManimColor("#B3EE3A")
OLIVEDRAB4 = ManimColor("#698B22")
ORANGE1 = ManimColor("#FFA500")
ORANGE2 = ManimColor("#EE9A00")
ORANGE3 = ManimColor("#CD8500")
ORANGE4 = ManimColor("#8B5A00")
ORANGERED1 = ManimColor("#FF4500")
ORANGERED2 = ManimColor("#EE4000")
ORANGERED3 = ManimColor("#CD3700")
ORANGERED4 = ManimColor("#8B2500")
ORCHID = ManimColor("#DA70D6")
ORCHID1 = ManimColor("#FF83FA")
ORCHID2 = ManimColor("#EE7AE9")
ORCHID3 = ManimColor("#CD69C9")
ORCHID4 = ManimColor("#8B4789")
PALE = ManimColor("#DB7093")
PALEGOLDENROD = ManimColor("#EEE8AA")
PALEGREEN = ManimColor("#98FB98")
PALEGREEN1 = ManimColor("#9AFF9A")
PALEGREEN2 = ManimColor("#90EE90")
PALEGREEN3 = ManimColor("#7CCD7C")
PALEGREEN4 = ManimColor("#548B54")
PALETURQUOISE = ManimColor("#AFEEEE")
PALETURQUOISE1 = ManimColor("#BBFFFF")
PALETURQUOISE2 = ManimColor("#AEEEEE")
PALETURQUOISE3 = ManimColor("#96CDCD")
PALETURQUOISE4 = ManimColor("#668B8B")
PALEVIOLETRED = ManimColor("#DB7093")
PALEVIOLETRED1 = ManimColor("#FF82AB")
PALEVIOLETRED2 = ManimColor("#EE799F")
PALEVIOLETRED3 = ManimColor("#CD6889")
PALEVIOLETRED4 = ManimColor("#8B475D")
PAPAYAWHIP = ManimColor("#FFEFD5")
PEACHPUFF1 = ManimColor("#FFDAB9")
PEACHPUFF2 = ManimColor("#EECBAD")
PEACHPUFF3 = ManimColor("#CDAF95")
PEACHPUFF4 = ManimColor("#8B7765")
PINK = ManimColor("#FFC0CB")
PINK1 = ManimColor("#FFB5C5")
PINK2 = ManimColor("#EEA9B8")
PINK3 = ManimColor("#CD919E")
PINK4 = ManimColor("#8B636C")
PLUM = ManimColor("#DDA0DD")
PLUM1 = ManimColor("#FFBBFF")
PLUM2 = ManimColor("#EEAEEE")
PLUM3 = ManimColor("#CD96CD")
PLUM4 = ManimColor("#8B668B")
POWDERBLUE = ManimColor("#B0E0E6")
PURPLE = ManimColor("#A020F0")
PURPLE1 = ManimColor("#9B30FF")
PURPLE2 = ManimColor("#912CEE")
PURPLE3 = ManimColor("#7D26CD")
PURPLE4 = ManimColor("#551A8B")
RED1 = ManimColor("#FF0000")
RED2 = ManimColor("#EE0000")
RED3 = ManimColor("#CD0000")
RED4 = ManimColor("#8B0000")
ROSYBROWN = ManimColor("#BC8F8F")
ROSYBROWN1 = ManimColor("#FFC1C1")
ROSYBROWN2 = ManimColor("#EEB4B4")
ROSYBROWN3 = ManimColor("#CD9B9B")
ROSYBROWN4 = ManimColor("#8B6969")
ROYALBLUE = ManimColor("#4169E1")
ROYALBLUE1 = ManimColor("#4876FF")
ROYALBLUE2 = ManimColor("#436EEE")
ROYALBLUE3 = ManimColor("#3A5FCD")
ROYALBLUE4 = ManimColor("#27408B")
SADDLEBROWN = ManimColor("#8B4513")
SALMON = ManimColor("#FA8072")
SALMON1 = ManimColor("#FF8C69")
SALMON2 = ManimColor("#EE8262")
SALMON3 = ManimColor("#CD7054")
SALMON4 = ManimColor("#8B4C39")
SANDYBROWN = ManimColor("#F4A460")
SEAGREEN1 = ManimColor("#54FF9F")
SEAGREEN2 = ManimColor("#4EEE94")
SEAGREEN3 = ManimColor("#43CD80")
SEAGREEN4 = ManimColor("#2E8B57")
SEASHELL1 = ManimColor("#FFF5EE")
SEASHELL2 = ManimColor("#EEE5DE")
SEASHELL3 = ManimColor("#CDC5BF")
SEASHELL4 = ManimColor("#8B8682")
SIENNA = ManimColor("#A0522D")
SIENNA1 = ManimColor("#FF8247")
SIENNA2 = ManimColor("#EE7942")
SIENNA3 = ManimColor("#CD6839")
SIENNA4 = ManimColor("#8B4726")
SKYBLUE = ManimColor("#87CEEB")
SKYBLUE1 = ManimColor("#87CEFF")
SKYBLUE2 = ManimColor("#7EC0EE")
SKYBLUE3 = ManimColor("#6CA6CD")
SKYBLUE4 = ManimColor("#4A708B")
SLATEBLUE = ManimColor("#6A5ACD")
SLATEBLUE1 = ManimColor("#836FFF")
SLATEBLUE2 = ManimColor("#7A67EE")
SLATEBLUE3 = ManimColor("#6959CD")
SLATEBLUE4 = ManimColor("#473C8B")
SLATEGRAY = ManimColor("#708090")
SLATEGRAY1 = ManimColor("#C6E2FF")
SLATEGRAY2 = ManimColor("#B9D3EE")
SLATEGRAY3 = ManimColor("#9FB6CD")
SLATEGRAY4 = ManimColor("#6C7B8B")
SNOW1 = ManimColor("#FFFAFA")
SNOW2 = ManimColor("#EEE9E9")
SNOW3 = ManimColor("#CDC9C9")
SNOW4 = ManimColor("#8B8989")
SPRINGGREEN1 = ManimColor("#00FF7F")
SPRINGGREEN2 = ManimColor("#00EE76")
SPRINGGREEN3 = ManimColor("#00CD66")
SPRINGGREEN4 = ManimColor("#008B45")
STEELBLUE = ManimColor("#4682B4")
STEELBLUE1 = ManimColor("#63B8FF")
STEELBLUE2 = ManimColor("#5CACEE")
STEELBLUE3 = ManimColor("#4F94CD")
STEELBLUE4 = ManimColor("#36648B")
TAN = ManimColor("#D2B48C")
TAN1 = ManimColor("#FFA54F")
TAN2 = ManimColor("#EE9A49")
TAN3 = ManimColor("#CD853F")
TAN4 = ManimColor("#8B5A2B")
THISTLE = ManimColor("#D8BFD8")
THISTLE1 = ManimColor("#FFE1FF")
THISTLE2 = ManimColor("#EED2EE")
THISTLE3 = ManimColor("#CDB5CD")
THISTLE4 = ManimColor("#8B7B8B")
TOMATO1 = ManimColor("#FF6347")
TOMATO2 = ManimColor("#EE5C42")
TOMATO3 = ManimColor("#CD4F39")
TOMATO4 = ManimColor("#8B3626")
TURQUOISE = ManimColor("#40E0D0")
TURQUOISE1 = ManimColor("#00F5FF")
TURQUOISE2 = ManimColor("#00E5EE")
TURQUOISE3 = ManimColor("#00C5CD")
TURQUOISE4 = ManimColor("#00868B")
VIOLET = ManimColor("#EE82EE")
VIOLETRED = ManimColor("#D02090")
VIOLETRED1 = ManimColor("#FF3E96")
VIOLETRED2 = ManimColor("#EE3A8C")
VIOLETRED3 = ManimColor("#CD3278")
VIOLETRED4 = ManimColor("#8B2252")
WHEAT = ManimColor("#F5DEB3")
WHEAT1 = ManimColor("#FFE7BA")
WHEAT2 = ManimColor("#EED8AE")
WHEAT3 = ManimColor("#CDBA96")
WHEAT4 = ManimColor("#8B7E66")
WHITE = ManimColor("#FFFFFF")
WHITESMOKE = ManimColor("#F5F5F5")
YELLOW1 = ManimColor("#FFFF00")
YELLOW2 = ManimColor("#EEEE00")
YELLOW3 = ManimColor("#CDCD00")
YELLOW4 = ManimColor("#8B8B00")
YELLOWGREEN = ManimColor("#9ACD32")

949
manim/utils/color/XKCD.py Normal file
View file

@ -0,0 +1,949 @@
"""Colors from the XKCD Color Name Survey
XKCD is a popular `web comic <https://xkcd.com/353/>`__ created by Randall Munroe.
His "`Color Name Survey <http://blog.xkcd.com/2010/05/03/color-survey-results/>`__" (with
200000 participants) resulted in a list of nearly 1000 color names.
While the ``XKCD`` module is exposed to Manim's global name space, the colors included
in it are not. This means that in order to use the colors, access them via the module name:
.. code:: pycon
>>> from manim import XKCD
>>> XKCD.MANGO
ManimColor('#FFA62B')
List of Color Constants
-----------------------
These hex values are non official approximate values intended to simulate the colors in HTML,
taken from https://www.w3schools.com/colors/colors_xkcd.asp.
.. automanimcolormodule:: manim.utils.color.XKCD
"""
from .core import ManimColor
ACIDGREEN = ManimColor("#8FFE09")
ADOBE = ManimColor("#BD6C48")
ALGAE = ManimColor("#54AC68")
ALGAEGREEN = ManimColor("#21C36F")
ALMOSTBLACK = ManimColor("#070D0D")
AMBER = ManimColor("#FEB308")
AMETHYST = ManimColor("#9B5FC0")
APPLE = ManimColor("#6ECB3C")
APPLEGREEN = ManimColor("#76CD26")
APRICOT = ManimColor("#FFB16D")
AQUA = ManimColor("#13EAC9")
AQUABLUE = ManimColor("#02D8E9")
AQUAGREEN = ManimColor("#12E193")
AQUAMARINE = ManimColor("#2EE8BB")
ARMYGREEN = ManimColor("#4B5D16")
ASPARAGUS = ManimColor("#77AB56")
AUBERGINE = ManimColor("#3D0734")
AUBURN = ManimColor("#9A3001")
AVOCADO = ManimColor("#90B134")
AVOCADOGREEN = ManimColor("#87A922")
AZUL = ManimColor("#1D5DEC")
AZURE = ManimColor("#069AF3")
BABYBLUE = ManimColor("#A2CFFE")
BABYGREEN = ManimColor("#8CFF9E")
BABYPINK = ManimColor("#FFB7CE")
BABYPOO = ManimColor("#AB9004")
BABYPOOP = ManimColor("#937C00")
BABYPOOPGREEN = ManimColor("#8F9805")
BABYPUKEGREEN = ManimColor("#B6C406")
BABYPURPLE = ManimColor("#CA9BF7")
BABYSHITBROWN = ManimColor("#AD900D")
BABYSHITGREEN = ManimColor("#889717")
BANANA = ManimColor("#FFFF7E")
BANANAYELLOW = ManimColor("#FAFE4B")
BARBIEPINK = ManimColor("#FE46A5")
BARFGREEN = ManimColor("#94AC02")
BARNEY = ManimColor("#AC1DB8")
BARNEYPURPLE = ManimColor("#A00498")
BATTLESHIPGREY = ManimColor("#6B7C85")
BEIGE = ManimColor("#E6DAA6")
BERRY = ManimColor("#990F4B")
BILE = ManimColor("#B5C306")
BLACK = ManimColor("#000000")
BLAND = ManimColor("#AFA88B")
BLOOD = ManimColor("#770001")
BLOODORANGE = ManimColor("#FE4B03")
BLOODRED = ManimColor("#980002")
BLUE = ManimColor("#0343DF")
BLUEBERRY = ManimColor("#464196")
BLUEBLUE = ManimColor("#2242C7")
BLUEGREEN = ManimColor("#0F9B8E")
BLUEGREY = ManimColor("#85A3B2")
BLUEPURPLE = ManimColor("#5A06EF")
BLUEVIOLET = ManimColor("#5D06E9")
BLUEWITHAHINTOFPURPLE = ManimColor("#533CC6")
BLUEYGREEN = ManimColor("#2BB179")
BLUEYGREY = ManimColor("#89A0B0")
BLUEYPURPLE = ManimColor("#6241C7")
BLUISH = ManimColor("#2976BB")
BLUISHGREEN = ManimColor("#10A674")
BLUISHGREY = ManimColor("#748B97")
BLUISHPURPLE = ManimColor("#703BE7")
BLURPLE = ManimColor("#5539CC")
BLUSH = ManimColor("#F29E8E")
BLUSHPINK = ManimColor("#FE828C")
BOOGER = ManimColor("#9BB53C")
BOOGERGREEN = ManimColor("#96B403")
BORDEAUX = ManimColor("#7B002C")
BORINGGREEN = ManimColor("#63B365")
BOTTLEGREEN = ManimColor("#044A05")
BRICK = ManimColor("#A03623")
BRICKORANGE = ManimColor("#C14A09")
BRICKRED = ManimColor("#8F1402")
BRIGHTAQUA = ManimColor("#0BF9EA")
BRIGHTBLUE = ManimColor("#0165FC")
BRIGHTCYAN = ManimColor("#41FDFE")
BRIGHTGREEN = ManimColor("#01FF07")
BRIGHTLAVENDER = ManimColor("#C760FF")
BRIGHTLIGHTBLUE = ManimColor("#26F7FD")
BRIGHTLIGHTGREEN = ManimColor("#2DFE54")
BRIGHTLILAC = ManimColor("#C95EFB")
BRIGHTLIME = ManimColor("#87FD05")
BRIGHTLIMEGREEN = ManimColor("#65FE08")
BRIGHTMAGENTA = ManimColor("#FF08E8")
BRIGHTOLIVE = ManimColor("#9CBB04")
BRIGHTORANGE = ManimColor("#FF5B00")
BRIGHTPINK = ManimColor("#FE01B1")
BRIGHTPURPLE = ManimColor("#BE03FD")
BRIGHTRED = ManimColor("#FF000D")
BRIGHTSEAGREEN = ManimColor("#05FFA6")
BRIGHTSKYBLUE = ManimColor("#02CCFE")
BRIGHTTEAL = ManimColor("#01F9C6")
BRIGHTTURQUOISE = ManimColor("#0FFEF9")
BRIGHTVIOLET = ManimColor("#AD0AFD")
BRIGHTYELLOW = ManimColor("#FFFD01")
BRIGHTYELLOWGREEN = ManimColor("#9DFF00")
BRITISHRACINGGREEN = ManimColor("#05480D")
BRONZE = ManimColor("#A87900")
BROWN = ManimColor("#653700")
BROWNGREEN = ManimColor("#706C11")
BROWNGREY = ManimColor("#8D8468")
BROWNISH = ManimColor("#9C6D57")
BROWNISHGREEN = ManimColor("#6A6E09")
BROWNISHGREY = ManimColor("#86775F")
BROWNISHORANGE = ManimColor("#CB7723")
BROWNISHPINK = ManimColor("#C27E79")
BROWNISHPURPLE = ManimColor("#76424E")
BROWNISHRED = ManimColor("#9E3623")
BROWNISHYELLOW = ManimColor("#C9B003")
BROWNORANGE = ManimColor("#B96902")
BROWNRED = ManimColor("#922B05")
BROWNYELLOW = ManimColor("#B29705")
BROWNYGREEN = ManimColor("#6F6C0A")
BROWNYORANGE = ManimColor("#CA6B02")
BRUISE = ManimColor("#7E4071")
BUBBLEGUM = ManimColor("#FF6CB5")
BUBBLEGUMPINK = ManimColor("#FF69AF")
BUFF = ManimColor("#FEF69E")
BURGUNDY = ManimColor("#610023")
BURNTORANGE = ManimColor("#C04E01")
BURNTRED = ManimColor("#9F2305")
BURNTSIENA = ManimColor("#B75203")
BURNTSIENNA = ManimColor("#B04E0F")
BURNTUMBER = ManimColor("#A0450E")
BURNTYELLOW = ManimColor("#D5AB09")
BURPLE = ManimColor("#6832E3")
BUTTER = ManimColor("#FFFF81")
BUTTERSCOTCH = ManimColor("#FDB147")
BUTTERYELLOW = ManimColor("#FFFD74")
CADETBLUE = ManimColor("#4E7496")
CAMEL = ManimColor("#C69F59")
CAMO = ManimColor("#7F8F4E")
CAMOGREEN = ManimColor("#526525")
CAMOUFLAGEGREEN = ManimColor("#4B6113")
CANARY = ManimColor("#FDFF63")
CANARYYELLOW = ManimColor("#FFFE40")
CANDYPINK = ManimColor("#FF63E9")
CARAMEL = ManimColor("#AF6F09")
CARMINE = ManimColor("#9D0216")
CARNATION = ManimColor("#FD798F")
CARNATIONPINK = ManimColor("#FF7FA7")
CAROLINABLUE = ManimColor("#8AB8FE")
CELADON = ManimColor("#BEFDB7")
CELERY = ManimColor("#C1FD95")
CEMENT = ManimColor("#A5A391")
CERISE = ManimColor("#DE0C62")
CERULEAN = ManimColor("#0485D1")
CERULEANBLUE = ManimColor("#056EEE")
CHARCOAL = ManimColor("#343837")
CHARCOALGREY = ManimColor("#3C4142")
CHARTREUSE = ManimColor("#C1F80A")
CHERRY = ManimColor("#CF0234")
CHERRYRED = ManimColor("#F7022A")
CHESTNUT = ManimColor("#742802")
CHOCOLATE = ManimColor("#3D1C02")
CHOCOLATEBROWN = ManimColor("#411900")
CINNAMON = ManimColor("#AC4F06")
CLARET = ManimColor("#680018")
CLAY = ManimColor("#B66A50")
CLAYBROWN = ManimColor("#B2713D")
CLEARBLUE = ManimColor("#247AFD")
COBALT = ManimColor("#1E488F")
COBALTBLUE = ManimColor("#030AA7")
COCOA = ManimColor("#875F42")
COFFEE = ManimColor("#A6814C")
COOLBLUE = ManimColor("#4984B8")
COOLGREEN = ManimColor("#33B864")
COOLGREY = ManimColor("#95A3A6")
COPPER = ManimColor("#B66325")
CORAL = ManimColor("#FC5A50")
CORALPINK = ManimColor("#FF6163")
CORNFLOWER = ManimColor("#6A79F7")
CORNFLOWERBLUE = ManimColor("#5170D7")
CRANBERRY = ManimColor("#9E003A")
CREAM = ManimColor("#FFFFC2")
CREME = ManimColor("#FFFFB6")
CRIMSON = ManimColor("#8C000F")
CUSTARD = ManimColor("#FFFD78")
CYAN = ManimColor("#00FFFF")
DANDELION = ManimColor("#FEDF08")
DARK = ManimColor("#1B2431")
DARKAQUA = ManimColor("#05696B")
DARKAQUAMARINE = ManimColor("#017371")
DARKBEIGE = ManimColor("#AC9362")
DARKBLUE = ManimColor("#030764")
DARKBLUEGREEN = ManimColor("#005249")
DARKBLUEGREY = ManimColor("#1F3B4D")
DARKBROWN = ManimColor("#341C02")
DARKCORAL = ManimColor("#CF524E")
DARKCREAM = ManimColor("#FFF39A")
DARKCYAN = ManimColor("#0A888A")
DARKFORESTGREEN = ManimColor("#002D04")
DARKFUCHSIA = ManimColor("#9D0759")
DARKGOLD = ManimColor("#B59410")
DARKGRASSGREEN = ManimColor("#388004")
DARKGREEN = ManimColor("#054907")
DARKGREENBLUE = ManimColor("#1F6357")
DARKGREY = ManimColor("#363737")
DARKGREYBLUE = ManimColor("#29465B")
DARKHOTPINK = ManimColor("#D90166")
DARKINDIGO = ManimColor("#1F0954")
DARKISHBLUE = ManimColor("#014182")
DARKISHGREEN = ManimColor("#287C37")
DARKISHPINK = ManimColor("#DA467D")
DARKISHPURPLE = ManimColor("#751973")
DARKISHRED = ManimColor("#A90308")
DARKKHAKI = ManimColor("#9B8F55")
DARKLAVENDER = ManimColor("#856798")
DARKLILAC = ManimColor("#9C6DA5")
DARKLIME = ManimColor("#84B701")
DARKLIMEGREEN = ManimColor("#7EBD01")
DARKMAGENTA = ManimColor("#960056")
DARKMAROON = ManimColor("#3C0008")
DARKMAUVE = ManimColor("#874C62")
DARKMINT = ManimColor("#48C072")
DARKMINTGREEN = ManimColor("#20C073")
DARKMUSTARD = ManimColor("#A88905")
DARKNAVY = ManimColor("#000435")
DARKNAVYBLUE = ManimColor("#00022E")
DARKOLIVE = ManimColor("#373E02")
DARKOLIVEGREEN = ManimColor("#3C4D03")
DARKORANGE = ManimColor("#C65102")
DARKPASTELGREEN = ManimColor("#56AE57")
DARKPEACH = ManimColor("#DE7E5D")
DARKPERIWINKLE = ManimColor("#665FD1")
DARKPINK = ManimColor("#CB416B")
DARKPLUM = ManimColor("#3F012C")
DARKPURPLE = ManimColor("#35063E")
DARKRED = ManimColor("#840000")
DARKROSE = ManimColor("#B5485D")
DARKROYALBLUE = ManimColor("#02066F")
DARKSAGE = ManimColor("#598556")
DARKSALMON = ManimColor("#C85A53")
DARKSAND = ManimColor("#A88F59")
DARKSEAFOAM = ManimColor("#1FB57A")
DARKSEAFOAMGREEN = ManimColor("#3EAF76")
DARKSEAGREEN = ManimColor("#11875D")
DARKSKYBLUE = ManimColor("#448EE4")
DARKSLATEBLUE = ManimColor("#214761")
DARKTAN = ManimColor("#AF884A")
DARKTAUPE = ManimColor("#7F684E")
DARKTEAL = ManimColor("#014D4E")
DARKTURQUOISE = ManimColor("#045C5A")
DARKVIOLET = ManimColor("#34013F")
DARKYELLOW = ManimColor("#D5B60A")
DARKYELLOWGREEN = ManimColor("#728F02")
DEEPAQUA = ManimColor("#08787F")
DEEPBLUE = ManimColor("#040273")
DEEPBROWN = ManimColor("#410200")
DEEPGREEN = ManimColor("#02590F")
DEEPLAVENDER = ManimColor("#8D5EB7")
DEEPLILAC = ManimColor("#966EBD")
DEEPMAGENTA = ManimColor("#A0025C")
DEEPORANGE = ManimColor("#DC4D01")
DEEPPINK = ManimColor("#CB0162")
DEEPPURPLE = ManimColor("#36013F")
DEEPRED = ManimColor("#9A0200")
DEEPROSE = ManimColor("#C74767")
DEEPSEABLUE = ManimColor("#015482")
DEEPSKYBLUE = ManimColor("#0D75F8")
DEEPTEAL = ManimColor("#00555A")
DEEPTURQUOISE = ManimColor("#017374")
DEEPVIOLET = ManimColor("#490648")
DENIM = ManimColor("#3B638C")
DENIMBLUE = ManimColor("#3B5B92")
DESERT = ManimColor("#CCAD60")
DIARRHEA = ManimColor("#9F8303")
DIRT = ManimColor("#8A6E45")
DIRTBROWN = ManimColor("#836539")
DIRTYBLUE = ManimColor("#3F829D")
DIRTYGREEN = ManimColor("#667E2C")
DIRTYORANGE = ManimColor("#C87606")
DIRTYPINK = ManimColor("#CA7B80")
DIRTYPURPLE = ManimColor("#734A65")
DIRTYYELLOW = ManimColor("#CDC50A")
DODGERBLUE = ManimColor("#3E82FC")
DRAB = ManimColor("#828344")
DRABGREEN = ManimColor("#749551")
DRIEDBLOOD = ManimColor("#4B0101")
DUCKEGGBLUE = ManimColor("#C3FBF4")
DULLBLUE = ManimColor("#49759C")
DULLBROWN = ManimColor("#876E4B")
DULLGREEN = ManimColor("#74A662")
DULLORANGE = ManimColor("#D8863B")
DULLPINK = ManimColor("#D5869D")
DULLPURPLE = ManimColor("#84597E")
DULLRED = ManimColor("#BB3F3F")
DULLTEAL = ManimColor("#5F9E8F")
DULLYELLOW = ManimColor("#EEDC5B")
DUSK = ManimColor("#4E5481")
DUSKBLUE = ManimColor("#26538D")
DUSKYBLUE = ManimColor("#475F94")
DUSKYPINK = ManimColor("#CC7A8B")
DUSKYPURPLE = ManimColor("#895B7B")
DUSKYROSE = ManimColor("#BA6873")
DUST = ManimColor("#B2996E")
DUSTYBLUE = ManimColor("#5A86AD")
DUSTYGREEN = ManimColor("#76A973")
DUSTYLAVENDER = ManimColor("#AC86A8")
DUSTYORANGE = ManimColor("#F0833A")
DUSTYPINK = ManimColor("#D58A94")
DUSTYPURPLE = ManimColor("#825F87")
DUSTYRED = ManimColor("#B9484E")
DUSTYROSE = ManimColor("#C0737A")
DUSTYTEAL = ManimColor("#4C9085")
EARTH = ManimColor("#A2653E")
EASTERGREEN = ManimColor("#8CFD7E")
EASTERPURPLE = ManimColor("#C071FE")
ECRU = ManimColor("#FEFFCA")
EGGPLANT = ManimColor("#380835")
EGGPLANTPURPLE = ManimColor("#430541")
EGGSHELL = ManimColor("#FFFCC4")
EGGSHELLBLUE = ManimColor("#C4FFF7")
ELECTRICBLUE = ManimColor("#0652FF")
ELECTRICGREEN = ManimColor("#21FC0D")
ELECTRICLIME = ManimColor("#A8FF04")
ELECTRICPINK = ManimColor("#FF0490")
ELECTRICPURPLE = ManimColor("#AA23FF")
EMERALD = ManimColor("#01A049")
EMERALDGREEN = ManimColor("#028F1E")
EVERGREEN = ManimColor("#05472A")
FADEDBLUE = ManimColor("#658CBB")
FADEDGREEN = ManimColor("#7BB274")
FADEDORANGE = ManimColor("#F0944D")
FADEDPINK = ManimColor("#DE9DAC")
FADEDPURPLE = ManimColor("#916E99")
FADEDRED = ManimColor("#D3494E")
FADEDYELLOW = ManimColor("#FEFF7F")
FAWN = ManimColor("#CFAF7B")
FERN = ManimColor("#63A950")
FERNGREEN = ManimColor("#548D44")
FIREENGINERED = ManimColor("#FE0002")
FLATBLUE = ManimColor("#3C73A8")
FLATGREEN = ManimColor("#699D4C")
FLUORESCENTGREEN = ManimColor("#08FF08")
FLUROGREEN = ManimColor("#0AFF02")
FOAMGREEN = ManimColor("#90FDA9")
FOREST = ManimColor("#0B5509")
FORESTGREEN = ManimColor("#06470C")
FORRESTGREEN = ManimColor("#154406")
FRENCHBLUE = ManimColor("#436BAD")
FRESHGREEN = ManimColor("#69D84F")
FROGGREEN = ManimColor("#58BC08")
FUCHSIA = ManimColor("#ED0DD9")
GOLD = ManimColor("#DBB40C")
GOLDEN = ManimColor("#F5BF03")
GOLDENBROWN = ManimColor("#B27A01")
GOLDENROD = ManimColor("#F9BC08")
GOLDENYELLOW = ManimColor("#FEC615")
GRAPE = ManimColor("#6C3461")
GRAPEFRUIT = ManimColor("#FD5956")
GRAPEPURPLE = ManimColor("#5D1451")
GRASS = ManimColor("#5CAC2D")
GRASSGREEN = ManimColor("#3F9B0B")
GRASSYGREEN = ManimColor("#419C03")
GREEN = ManimColor("#15B01A")
GREENAPPLE = ManimColor("#5EDC1F")
GREENBLUE = ManimColor("#01C08D")
GREENBROWN = ManimColor("#544E03")
GREENGREY = ManimColor("#77926F")
GREENISH = ManimColor("#40A368")
GREENISHBEIGE = ManimColor("#C9D179")
GREENISHBLUE = ManimColor("#0B8B87")
GREENISHBROWN = ManimColor("#696112")
GREENISHCYAN = ManimColor("#2AFEB7")
GREENISHGREY = ManimColor("#96AE8D")
GREENISHTAN = ManimColor("#BCCB7A")
GREENISHTEAL = ManimColor("#32BF84")
GREENISHTURQUOISE = ManimColor("#00FBB0")
GREENISHYELLOW = ManimColor("#CDFD02")
GREENTEAL = ManimColor("#0CB577")
GREENYBLUE = ManimColor("#42B395")
GREENYBROWN = ManimColor("#696006")
GREENYELLOW = ManimColor("#B5CE08")
GREENYGREY = ManimColor("#7EA07A")
GREENYYELLOW = ManimColor("#C6F808")
GREY = ManimColor("#929591")
GREYBLUE = ManimColor("#647D8E")
GREYBROWN = ManimColor("#7F7053")
GREYGREEN = ManimColor("#86A17D")
GREYISH = ManimColor("#A8A495")
GREYISHBLUE = ManimColor("#5E819D")
GREYISHBROWN = ManimColor("#7A6A4F")
GREYISHGREEN = ManimColor("#82A67D")
GREYISHPINK = ManimColor("#C88D94")
GREYISHPURPLE = ManimColor("#887191")
GREYISHTEAL = ManimColor("#719F91")
GREYPINK = ManimColor("#C3909B")
GREYPURPLE = ManimColor("#826D8C")
GREYTEAL = ManimColor("#5E9B8A")
GROSSGREEN = ManimColor("#A0BF16")
GUNMETAL = ManimColor("#536267")
HAZEL = ManimColor("#8E7618")
HEATHER = ManimColor("#A484AC")
HELIOTROPE = ManimColor("#D94FF5")
HIGHLIGHTERGREEN = ManimColor("#1BFC06")
HOSPITALGREEN = ManimColor("#9BE5AA")
HOTGREEN = ManimColor("#25FF29")
HOTMAGENTA = ManimColor("#F504C9")
HOTPINK = ManimColor("#FF028D")
HOTPURPLE = ManimColor("#CB00F5")
HUNTERGREEN = ManimColor("#0B4008")
ICE = ManimColor("#D6FFFA")
ICEBLUE = ManimColor("#D7FFFE")
ICKYGREEN = ManimColor("#8FAE22")
INDIANRED = ManimColor("#850E04")
INDIGO = ManimColor("#380282")
INDIGOBLUE = ManimColor("#3A18B1")
IRIS = ManimColor("#6258C4")
IRISHGREEN = ManimColor("#019529")
IVORY = ManimColor("#FFFFCB")
JADE = ManimColor("#1FA774")
JADEGREEN = ManimColor("#2BAF6A")
JUNGLEGREEN = ManimColor("#048243")
KELLEYGREEN = ManimColor("#009337")
KELLYGREEN = ManimColor("#02AB2E")
KERMITGREEN = ManimColor("#5CB200")
KEYLIME = ManimColor("#AEFF6E")
KHAKI = ManimColor("#AAA662")
KHAKIGREEN = ManimColor("#728639")
KIWI = ManimColor("#9CEF43")
KIWIGREEN = ManimColor("#8EE53F")
LAVENDER = ManimColor("#C79FEF")
LAVENDERBLUE = ManimColor("#8B88F8")
LAVENDERPINK = ManimColor("#DD85D7")
LAWNGREEN = ManimColor("#4DA409")
LEAF = ManimColor("#71AA34")
LEAFGREEN = ManimColor("#5CA904")
LEAFYGREEN = ManimColor("#51B73B")
LEATHER = ManimColor("#AC7434")
LEMON = ManimColor("#FDFF52")
LEMONGREEN = ManimColor("#ADF802")
LEMONLIME = ManimColor("#BFFE28")
LEMONYELLOW = ManimColor("#FDFF38")
LICHEN = ManimColor("#8FB67B")
LIGHTAQUA = ManimColor("#8CFFDB")
LIGHTAQUAMARINE = ManimColor("#7BFDC7")
LIGHTBEIGE = ManimColor("#FFFEB6")
LIGHTBLUE = ManimColor("#7BC8F6")
LIGHTBLUEGREEN = ManimColor("#7EFBB3")
LIGHTBLUEGREY = ManimColor("#B7C9E2")
LIGHTBLUISHGREEN = ManimColor("#76FDA8")
LIGHTBRIGHTGREEN = ManimColor("#53FE5C")
LIGHTBROWN = ManimColor("#AD8150")
LIGHTBURGUNDY = ManimColor("#A8415B")
LIGHTCYAN = ManimColor("#ACFFFC")
LIGHTEGGPLANT = ManimColor("#894585")
LIGHTERGREEN = ManimColor("#75FD63")
LIGHTERPURPLE = ManimColor("#A55AF4")
LIGHTFORESTGREEN = ManimColor("#4F9153")
LIGHTGOLD = ManimColor("#FDDC5C")
LIGHTGRASSGREEN = ManimColor("#9AF764")
LIGHTGREEN = ManimColor("#76FF7B")
LIGHTGREENBLUE = ManimColor("#56FCA2")
LIGHTGREENISHBLUE = ManimColor("#63F7B4")
LIGHTGREY = ManimColor("#D8DCD6")
LIGHTGREYBLUE = ManimColor("#9DBCD4")
LIGHTGREYGREEN = ManimColor("#B7E1A1")
LIGHTINDIGO = ManimColor("#6D5ACF")
LIGHTISHBLUE = ManimColor("#3D7AFD")
LIGHTISHGREEN = ManimColor("#61E160")
LIGHTISHPURPLE = ManimColor("#A552E6")
LIGHTISHRED = ManimColor("#FE2F4A")
LIGHTKHAKI = ManimColor("#E6F2A2")
LIGHTLAVENDAR = ManimColor("#EFC0FE")
LIGHTLAVENDER = ManimColor("#DFC5FE")
LIGHTLIGHTBLUE = ManimColor("#CAFFFB")
LIGHTLIGHTGREEN = ManimColor("#C8FFB0")
LIGHTLILAC = ManimColor("#EDC8FF")
LIGHTLIME = ManimColor("#AEFD6C")
LIGHTLIMEGREEN = ManimColor("#B9FF66")
LIGHTMAGENTA = ManimColor("#FA5FF7")
LIGHTMAROON = ManimColor("#A24857")
LIGHTMAUVE = ManimColor("#C292A1")
LIGHTMINT = ManimColor("#B6FFBB")
LIGHTMINTGREEN = ManimColor("#A6FBB2")
LIGHTMOSSGREEN = ManimColor("#A6C875")
LIGHTMUSTARD = ManimColor("#F7D560")
LIGHTNAVY = ManimColor("#155084")
LIGHTNAVYBLUE = ManimColor("#2E5A88")
LIGHTNEONGREEN = ManimColor("#4EFD54")
LIGHTOLIVE = ManimColor("#ACBF69")
LIGHTOLIVEGREEN = ManimColor("#A4BE5C")
LIGHTORANGE = ManimColor("#FDAA48")
LIGHTPASTELGREEN = ManimColor("#B2FBA5")
LIGHTPEACH = ManimColor("#FFD8B1")
LIGHTPEAGREEN = ManimColor("#C4FE82")
LIGHTPERIWINKLE = ManimColor("#C1C6FC")
LIGHTPINK = ManimColor("#FFD1DF")
LIGHTPLUM = ManimColor("#9D5783")
LIGHTPURPLE = ManimColor("#BF77F6")
LIGHTRED = ManimColor("#FF474C")
LIGHTROSE = ManimColor("#FFC5CB")
LIGHTROYALBLUE = ManimColor("#3A2EFE")
LIGHTSAGE = ManimColor("#BCECAC")
LIGHTSALMON = ManimColor("#FEA993")
LIGHTSEAFOAM = ManimColor("#A0FEBF")
LIGHTSEAFOAMGREEN = ManimColor("#a7ffb5")
LIGHTSEAGREEN = ManimColor("#98F6B0")
LIGHTSKYBLUE = ManimColor("#C6FCFF")
LIGHTTAN = ManimColor("#FBEEAC")
LIGHTTEAL = ManimColor("#90E4C1")
LIGHTTURQUOISE = ManimColor("#7EF4CC")
LIGHTURPLE = ManimColor("#B36FF6")
LIGHTVIOLET = ManimColor("#D6B4FC")
LIGHTYELLOW = ManimColor("#FFFE7A")
LIGHTYELLOWGREEN = ManimColor("#CCFD7F")
LIGHTYELLOWISHGREEN = ManimColor("#C2FF89")
LILAC = ManimColor("#CEA2FD")
LILIAC = ManimColor("#C48EFD")
LIME = ManimColor("#AAFF32")
LIMEGREEN = ManimColor("#89FE05")
LIMEYELLOW = ManimColor("#D0FE1D")
LIPSTICK = ManimColor("#D5174E")
LIPSTICKRED = ManimColor("#C0022F")
MACARONIANDCHEESE = ManimColor("#EFB435")
MAGENTA = ManimColor("#C20078")
MAHOGANY = ManimColor("#4A0100")
MAIZE = ManimColor("#F4D054")
MANGO = ManimColor("#FFA62B")
MANILLA = ManimColor("#FFFA86")
MARIGOLD = ManimColor("#FCC006")
MARINE = ManimColor("#042E60")
MARINEBLUE = ManimColor("#01386A")
MAROON = ManimColor("#650021")
MAUVE = ManimColor("#AE7181")
MEDIUMBLUE = ManimColor("#2C6FBB")
MEDIUMBROWN = ManimColor("#7F5112")
MEDIUMGREEN = ManimColor("#39AD48")
MEDIUMGREY = ManimColor("#7D7F7C")
MEDIUMPINK = ManimColor("#F36196")
MEDIUMPURPLE = ManimColor("#9E43A2")
MELON = ManimColor("#FF7855")
MERLOT = ManimColor("#730039")
METALLICBLUE = ManimColor("#4F738E")
MIDBLUE = ManimColor("#276AB3")
MIDGREEN = ManimColor("#50A747")
MIDNIGHT = ManimColor("#03012D")
MIDNIGHTBLUE = ManimColor("#020035")
MIDNIGHTPURPLE = ManimColor("#280137")
MILITARYGREEN = ManimColor("#667C3E")
MILKCHOCOLATE = ManimColor("#7F4E1E")
MINT = ManimColor("#9FFEB0")
MINTGREEN = ManimColor("#8FFF9F")
MINTYGREEN = ManimColor("#0BF77D")
MOCHA = ManimColor("#9D7651")
MOSS = ManimColor("#769958")
MOSSGREEN = ManimColor("#658B38")
MOSSYGREEN = ManimColor("#638B27")
MUD = ManimColor("#735C12")
MUDBROWN = ManimColor("#60460F")
MUDDYBROWN = ManimColor("#886806")
MUDDYGREEN = ManimColor("#657432")
MUDDYYELLOW = ManimColor("#BFAC05")
MUDGREEN = ManimColor("#606602")
MULBERRY = ManimColor("#920A4E")
MURKYGREEN = ManimColor("#6C7A0E")
MUSHROOM = ManimColor("#BA9E88")
MUSTARD = ManimColor("#CEB301")
MUSTARDBROWN = ManimColor("#AC7E04")
MUSTARDGREEN = ManimColor("#A8B504")
MUSTARDYELLOW = ManimColor("#D2BD0A")
MUTEDBLUE = ManimColor("#3B719F")
MUTEDGREEN = ManimColor("#5FA052")
MUTEDPINK = ManimColor("#D1768F")
MUTEDPURPLE = ManimColor("#805B87")
NASTYGREEN = ManimColor("#70B23F")
NAVY = ManimColor("#01153E")
NAVYBLUE = ManimColor("#001146")
NAVYGREEN = ManimColor("#35530A")
NEONBLUE = ManimColor("#04D9FF")
NEONGREEN = ManimColor("#0CFF0C")
NEONPINK = ManimColor("#FE019A")
NEONPURPLE = ManimColor("#BC13FE")
NEONRED = ManimColor("#FF073A")
NEONYELLOW = ManimColor("#CFFF04")
NICEBLUE = ManimColor("#107AB0")
NIGHTBLUE = ManimColor("#040348")
OCEAN = ManimColor("#017B92")
OCEANBLUE = ManimColor("#03719C")
OCEANGREEN = ManimColor("#3D9973")
OCHER = ManimColor("#BF9B0C")
OCHRE = ManimColor("#BF9005")
OCRE = ManimColor("#C69C04")
OFFBLUE = ManimColor("#5684AE")
OFFGREEN = ManimColor("#6BA353")
OFFWHITE = ManimColor("#FFFFE4")
OFFYELLOW = ManimColor("#F1F33F")
OLDPINK = ManimColor("#C77986")
OLDROSE = ManimColor("#C87F89")
OLIVE = ManimColor("#6E750E")
OLIVEBROWN = ManimColor("#645403")
OLIVEDRAB = ManimColor("#6F7632")
OLIVEGREEN = ManimColor("#677A04")
OLIVEYELLOW = ManimColor("#C2B709")
ORANGE = ManimColor("#F97306")
ORANGEBROWN = ManimColor("#BE6400")
ORANGEISH = ManimColor("#FD8D49")
ORANGEPINK = ManimColor("#FF6F52")
ORANGERED = ManimColor("#FE420F")
ORANGEYBROWN = ManimColor("#B16002")
ORANGEYELLOW = ManimColor("#FFAD01")
ORANGEYRED = ManimColor("#FA4224")
ORANGEYYELLOW = ManimColor("#FDB915")
ORANGISH = ManimColor("#FC824A")
ORANGISHBROWN = ManimColor("#B25F03")
ORANGISHRED = ManimColor("#F43605")
ORCHID = ManimColor("#C875C4")
PALE = ManimColor("#FFF9D0")
PALEAQUA = ManimColor("#B8FFEB")
PALEBLUE = ManimColor("#D0FEFE")
PALEBROWN = ManimColor("#B1916E")
PALECYAN = ManimColor("#B7FFFA")
PALEGOLD = ManimColor("#FDDE6C")
PALEGREEN = ManimColor("#C7FDB5")
PALEGREY = ManimColor("#FDFDFE")
PALELAVENDER = ManimColor("#EECFFE")
PALELIGHTGREEN = ManimColor("#B1FC99")
PALELILAC = ManimColor("#E4CBFF")
PALELIME = ManimColor("#BEFD73")
PALELIMEGREEN = ManimColor("#B1FF65")
PALEMAGENTA = ManimColor("#D767AD")
PALEMAUVE = ManimColor("#FED0FC")
PALEOLIVE = ManimColor("#B9CC81")
PALEOLIVEGREEN = ManimColor("#B1D27B")
PALEORANGE = ManimColor("#FFA756")
PALEPEACH = ManimColor("#FFE5AD")
PALEPINK = ManimColor("#FFCFDC")
PALEPURPLE = ManimColor("#B790D4")
PALERED = ManimColor("#D9544D")
PALEROSE = ManimColor("#FDC1C5")
PALESALMON = ManimColor("#FFB19A")
PALESKYBLUE = ManimColor("#BDF6FE")
PALETEAL = ManimColor("#82CBB2")
PALETURQUOISE = ManimColor("#A5FBD5")
PALEVIOLET = ManimColor("#CEAEFA")
PALEYELLOW = ManimColor("#FFFF84")
PARCHMENT = ManimColor("#FEFCAF")
PASTELBLUE = ManimColor("#A2BFFE")
PASTELGREEN = ManimColor("#B0FF9D")
PASTELORANGE = ManimColor("#FF964F")
PASTELPINK = ManimColor("#FFBACD")
PASTELPURPLE = ManimColor("#CAA0FF")
PASTELRED = ManimColor("#DB5856")
PASTELYELLOW = ManimColor("#FFFE71")
PEA = ManimColor("#A4BF20")
PEACH = ManimColor("#FFB07C")
PEACHYPINK = ManimColor("#FF9A8A")
PEACOCKBLUE = ManimColor("#016795")
PEAGREEN = ManimColor("#8EAB12")
PEAR = ManimColor("#CBF85F")
PEASOUP = ManimColor("#929901")
PEASOUPGREEN = ManimColor("#94A617")
PERIWINKLE = ManimColor("#8E82FE")
PERIWINKLEBLUE = ManimColor("#8F99FB")
PERRYWINKLE = ManimColor("#8F8CE7")
PETROL = ManimColor("#005F6A")
PIGPINK = ManimColor("#E78EA5")
PINE = ManimColor("#2B5D34")
PINEGREEN = ManimColor("#0A481E")
PINK = ManimColor("#FF81C0")
PINKISH = ManimColor("#D46A7E")
PINKISHBROWN = ManimColor("#B17261")
PINKISHGREY = ManimColor("#C8ACA9")
PINKISHORANGE = ManimColor("#FF724C")
PINKISHPURPLE = ManimColor("#D648D7")
PINKISHRED = ManimColor("#F10C45")
PINKISHTAN = ManimColor("#D99B82")
PINKPURPLE = ManimColor("#EF1DE7")
PINKRED = ManimColor("#F5054F")
PINKY = ManimColor("#FC86AA")
PINKYPURPLE = ManimColor("#C94CBE")
PINKYRED = ManimColor("#FC2647")
PISSYELLOW = ManimColor("#DDD618")
PISTACHIO = ManimColor("#C0FA8B")
PLUM = ManimColor("#580F41")
PLUMPURPLE = ManimColor("#4E0550")
POISONGREEN = ManimColor("#40FD14")
POO = ManimColor("#8F7303")
POOBROWN = ManimColor("#885F01")
POOP = ManimColor("#7F5E00")
POOPBROWN = ManimColor("#7A5901")
POOPGREEN = ManimColor("#6F7C00")
POWDERBLUE = ManimColor("#B1D1FC")
POWDERPINK = ManimColor("#FFB2D0")
PRIMARYBLUE = ManimColor("#0804F9")
PRUSSIANBLUE = ManimColor("#004577")
PUCE = ManimColor("#A57E52")
PUKE = ManimColor("#A5A502")
PUKEBROWN = ManimColor("#947706")
PUKEGREEN = ManimColor("#9AAE07")
PUKEYELLOW = ManimColor("#C2BE0E")
PUMPKIN = ManimColor("#E17701")
PUMPKINORANGE = ManimColor("#FB7D07")
PUREBLUE = ManimColor("#0203E2")
PURPLE = ManimColor("#7E1E9C")
PURPLEBLUE = ManimColor("#5D21D0")
PURPLEBROWN = ManimColor("#673A3F")
PURPLEGREY = ManimColor("#866F85")
PURPLEISH = ManimColor("#98568D")
PURPLEISHBLUE = ManimColor("#6140EF")
PURPLEISHPINK = ManimColor("#DF4EC8")
PURPLEPINK = ManimColor("#D725DE")
PURPLERED = ManimColor("#990147")
PURPLEY = ManimColor("#8756E4")
PURPLEYBLUE = ManimColor("#5F34E7")
PURPLEYGREY = ManimColor("#947E94")
PURPLEYPINK = ManimColor("#C83CB9")
PURPLISH = ManimColor("#94568C")
PURPLISHBLUE = ManimColor("#601EF9")
PURPLISHBROWN = ManimColor("#6B4247")
PURPLISHGREY = ManimColor("#7A687F")
PURPLISHPINK = ManimColor("#CE5DAE")
PURPLISHRED = ManimColor("#B0054B")
PURPLY = ManimColor("#983FB2")
PURPLYBLUE = ManimColor("#661AEE")
PURPLYPINK = ManimColor("#F075E6")
PUTTY = ManimColor("#BEAE8A")
RACINGGREEN = ManimColor("#014600")
RADIOACTIVEGREEN = ManimColor("#2CFA1F")
RASPBERRY = ManimColor("#B00149")
RAWSIENNA = ManimColor("#9A6200")
RAWUMBER = ManimColor("#A75E09")
REALLYLIGHTBLUE = ManimColor("#D4FFFF")
RED = ManimColor("#E50000")
REDBROWN = ManimColor("#8B2E16")
REDDISH = ManimColor("#C44240")
REDDISHBROWN = ManimColor("#7F2B0A")
REDDISHGREY = ManimColor("#997570")
REDDISHORANGE = ManimColor("#F8481C")
REDDISHPINK = ManimColor("#FE2C54")
REDDISHPURPLE = ManimColor("#910951")
REDDYBROWN = ManimColor("#6E1005")
REDORANGE = ManimColor("#FD3C06")
REDPINK = ManimColor("#FA2A55")
REDPURPLE = ManimColor("#820747")
REDVIOLET = ManimColor("#9E0168")
REDWINE = ManimColor("#8C0034")
RICHBLUE = ManimColor("#021BF9")
RICHPURPLE = ManimColor("#720058")
ROBINEGGBLUE = ManimColor("#8AF1FE")
ROBINSEGG = ManimColor("#6DEDFD")
ROBINSEGGBLUE = ManimColor("#98EFF9")
ROSA = ManimColor("#FE86A4")
ROSE = ManimColor("#CF6275")
ROSEPINK = ManimColor("#F7879A")
ROSERED = ManimColor("#BE013C")
ROSYPINK = ManimColor("#F6688E")
ROGUE = ManimColor("#AB1239")
ROYAL = ManimColor("#0C1793")
ROYALBLUE = ManimColor("#0504AA")
ROYALPURPLE = ManimColor("#4B006E")
RUBY = ManimColor("#CA0147")
RUSSET = ManimColor("#A13905")
RUST = ManimColor("#A83C09")
RUSTBROWN = ManimColor("#8B3103")
RUSTORANGE = ManimColor("#C45508")
RUSTRED = ManimColor("#AA2704")
RUSTYORANGE = ManimColor("#CD5909")
RUSTYRED = ManimColor("#AF2F0D")
SAFFRON = ManimColor("#FEB209")
SAGE = ManimColor("#87AE73")
SAGEGREEN = ManimColor("#88B378")
SALMON = ManimColor("#FF796C")
SALMONPINK = ManimColor("#FE7B7C")
SAND = ManimColor("#E2CA76")
SANDBROWN = ManimColor("#CBA560")
SANDSTONE = ManimColor("#C9AE74")
SANDY = ManimColor("#F1DA7A")
SANDYBROWN = ManimColor("#C4A661")
SANDYELLOW = ManimColor("#FCE166")
SANDYYELLOW = ManimColor("#FDEE73")
SAPGREEN = ManimColor("#5C8B15")
SAPPHIRE = ManimColor("#2138AB")
SCARLET = ManimColor("#BE0119")
SEA = ManimColor("#3C9992")
SEABLUE = ManimColor("#047495")
SEAFOAM = ManimColor("#80F9AD")
SEAFOAMBLUE = ManimColor("#78D1B6")
SEAFOAMGREEN = ManimColor("#7AF9AB")
SEAGREEN = ManimColor("#53FCA1")
SEAWEED = ManimColor("#18D17B")
SEAWEEDGREEN = ManimColor("#35AD6B")
SEPIA = ManimColor("#985E2B")
SHAMROCK = ManimColor("#01B44C")
SHAMROCKGREEN = ManimColor("#02C14D")
SHIT = ManimColor("#7F5F00")
SHITBROWN = ManimColor("#7B5804")
SHITGREEN = ManimColor("#758000")
SHOCKINGPINK = ManimColor("#FE02A2")
SICKGREEN = ManimColor("#9DB92C")
SICKLYGREEN = ManimColor("#94B21C")
SICKLYYELLOW = ManimColor("#D0E429")
SIENNA = ManimColor("#A9561E")
SILVER = ManimColor("#C5C9C7")
SKY = ManimColor("#82CAFC")
SKYBLUE = ManimColor("#75BBFD")
SLATE = ManimColor("#516572")
SLATEBLUE = ManimColor("#5B7C99")
SLATEGREEN = ManimColor("#658D6D")
SLATEGREY = ManimColor("#59656D")
SLIMEGREEN = ManimColor("#99CC04")
SNOT = ManimColor("#ACBB0D")
SNOTGREEN = ManimColor("#9DC100")
SOFTBLUE = ManimColor("#6488EA")
SOFTGREEN = ManimColor("#6FC276")
SOFTPINK = ManimColor("#FDB0C0")
SOFTPURPLE = ManimColor("#A66FB5")
SPEARMINT = ManimColor("#1EF876")
SPRINGGREEN = ManimColor("#A9F971")
SPRUCE = ManimColor("#0A5F38")
SQUASH = ManimColor("#F2AB15")
STEEL = ManimColor("#738595")
STEELBLUE = ManimColor("#5A7D9A")
STEELGREY = ManimColor("#6F828A")
STONE = ManimColor("#ADA587")
STORMYBLUE = ManimColor("#507B9C")
STRAW = ManimColor("#FCF679")
STRAWBERRY = ManimColor("#FB2943")
STRONGBLUE = ManimColor("#0C06F7")
STRONGPINK = ManimColor("#FF0789")
SUNFLOWER = ManimColor("#FFC512")
SUNFLOWERYELLOW = ManimColor("#FFDA03")
SUNNYYELLOW = ManimColor("#FFF917")
SUNSHINEYELLOW = ManimColor("#FFFD37")
SUNYELLOW = ManimColor("#FFDF22")
SWAMP = ManimColor("#698339")
SWAMPGREEN = ManimColor("#748500")
TAN = ManimColor("#D1B26F")
TANBROWN = ManimColor("#AB7E4C")
TANGERINE = ManimColor("#FF9408")
TANGREEN = ManimColor("#A9BE70")
TAUPE = ManimColor("#B9A281")
TEA = ManimColor("#65AB7C")
TEAGREEN = ManimColor("#BDF8A3")
TEAL = ManimColor("#029386")
TEALBLUE = ManimColor("#01889F")
TEALGREEN = ManimColor("#25A36F")
TEALISH = ManimColor("#24BCA8")
TEALISHGREEN = ManimColor("#0CDC73")
TERRACOTA = ManimColor("#CB6843")
TERRACOTTA = ManimColor("#C9643B")
TIFFANYBLUE = ManimColor("#7BF2DA")
TOMATO = ManimColor("#EF4026")
TOMATORED = ManimColor("#EC2D01")
TOPAZ = ManimColor("#13BBAF")
TOUPE = ManimColor("#C7AC7D")
TOXICGREEN = ManimColor("#61DE2A")
TREEGREEN = ManimColor("#2A7E19")
TRUEBLUE = ManimColor("#010FCC")
TRUEGREEN = ManimColor("#089404")
TURQUOISE = ManimColor("#06C2AC")
TURQUOISEBLUE = ManimColor("#06B1C4")
TURQUOISEGREEN = ManimColor("#04F489")
TURTLEGREEN = ManimColor("#75B84F")
TWILIGHT = ManimColor("#4E518B")
TWILIGHTBLUE = ManimColor("#0A437A")
UGLYBLUE = ManimColor("#31668A")
UGLYBROWN = ManimColor("#7D7103")
UGLYGREEN = ManimColor("#7A9703")
UGLYPINK = ManimColor("#CD7584")
UGLYPURPLE = ManimColor("#A442A0")
UGLYYELLOW = ManimColor("#D0C101")
ULTRAMARINE = ManimColor("#2000B1")
ULTRAMARINEBLUE = ManimColor("#1805DB")
UMBER = ManimColor("#B26400")
VELVET = ManimColor("#750851")
VERMILION = ManimColor("#F4320C")
VERYDARKBLUE = ManimColor("#000133")
VERYDARKBROWN = ManimColor("#1D0200")
VERYDARKGREEN = ManimColor("#062E03")
VERYDARKPURPLE = ManimColor("#2A0134")
VERYLIGHTBLUE = ManimColor("#D5FFFF")
VERYLIGHTBROWN = ManimColor("#D3B683")
VERYLIGHTGREEN = ManimColor("#D1FFBD")
VERYLIGHTPINK = ManimColor("#FFF4F2")
VERYLIGHTPURPLE = ManimColor("#F6CEFC")
VERYPALEBLUE = ManimColor("#D6FFFE")
VERYPALEGREEN = ManimColor("#CFFDBC")
VIBRANTBLUE = ManimColor("#0339F8")
VIBRANTGREEN = ManimColor("#0ADD08")
VIBRANTPURPLE = ManimColor("#AD03DE")
VIOLET = ManimColor("#9A0EEA")
VIOLETBLUE = ManimColor("#510AC9")
VIOLETPINK = ManimColor("#FB5FFC")
VIOLETRED = ManimColor("#A50055")
VIRIDIAN = ManimColor("#1E9167")
VIVIDBLUE = ManimColor("#152EFF")
VIVIDGREEN = ManimColor("#2FEF10")
VIVIDPURPLE = ManimColor("#9900FA")
VOMIT = ManimColor("#A2A415")
VOMITGREEN = ManimColor("#89A203")
VOMITYELLOW = ManimColor("#C7C10C")
WARMBLUE = ManimColor("#4B57DB")
WARMBROWN = ManimColor("#964E02")
WARMGREY = ManimColor("#978A84")
WARMPINK = ManimColor("#FB5581")
WARMPURPLE = ManimColor("#952E8F")
WASHEDOUTGREEN = ManimColor("#BCF5A6")
WATERBLUE = ManimColor("#0E87CC")
WATERMELON = ManimColor("#FD4659")
WEIRDGREEN = ManimColor("#3AE57F")
WHEAT = ManimColor("#FBDD7E")
WHITE = ManimColor("#FFFFFF")
WINDOWSBLUE = ManimColor("#3778BF")
WINE = ManimColor("#80013F")
WINERED = ManimColor("#7B0323")
WINTERGREEN = ManimColor("#20F986")
WISTERIA = ManimColor("#A87DC2")
YELLOW = ManimColor("#FFFF14")
YELLOWBROWN = ManimColor("#B79400")
YELLOWGREEN = ManimColor("#BBF90F")
YELLOWISH = ManimColor("#FAEE66")
YELLOWISHBROWN = ManimColor("#9B7A01")
YELLOWISHGREEN = ManimColor("#B0DD16")
YELLOWISHORANGE = ManimColor("#FFAB0F")
YELLOWISHTAN = ManimColor("#FCFC81")
YELLOWOCHRE = ManimColor("#CB9D06")
YELLOWORANGE = ManimColor("#FCB001")
YELLOWTAN = ManimColor("#FFE36E")
YELLOWYBROWN = ManimColor("#AE8B0C")
YELLOWYGREEN = ManimColor("#BFF128")

View file

@ -0,0 +1,58 @@
"""Utilities for working with colors and predefined color constants.
Color data structure
--------------------
.. autosummary::
:toctree: ../reference
core
Predefined colors
-----------------
There are several predefined colors available in Manim:
- The colors listed in :mod:`.color.manim_colors` are loaded into
Manim's global name space.
- The colors in :mod:`.color.AS2700`, :mod:`.color.BS381`, :mod:`.color.X11`,
and :mod:`.color.XKCD` need to be accessed via their module (which are available
in Manim's global name space), or imported separately. For example:
.. code:: pycon
>>> from manim import XKCD
>>> XKCD.AVOCADO
ManimColor('#90B134')
Or, alternatively:
.. code:: pycon
>>> from manim.utils.color.XKCD import AVOCADO
>>> AVOCADO
ManimColor('#90B134')
The following modules contain the predefined color constants:
.. autosummary::
:toctree: ../reference
manim_colors
AS2700
BS381
XKCD
X11
"""
from typing import Dict, List
from . import AS2700, BS381, X11, XKCD
from .core import *
from .manim_colors import *
_all_color_dict: Dict[str, ManimColor] = {
k: v for k, v in globals().items() if isinstance(v, ManimColor)
}

998
manim/utils/color/core.py Normal file
View file

@ -0,0 +1,998 @@
"""Manim's (internal) color data structure and some utilities for
color conversion.
This module contains the implementation of :class:`.ManimColor`,
the data structure internally used to represent colors.
"""
from __future__ import annotations
# logger = _config.logger
import colorsys
import random
from typing import Any, Sequence, Union
import numpy as np
from typing_extensions import Literal, TypeAlias
from ...utils.space_ops import normalize
ManimColorDType: TypeAlias = np.float64
ManimFloat: TypeAlias = np.float64
ManimInt: TypeAlias = np.int64
RGB_Array_Float: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimFloat]]"
RGB_Tuple_Float: TypeAlias = "tuple[float, float, float]"
RGB_Array_Int: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimInt]]"
RGB_Tuple_Int: TypeAlias = "tuple[int, int, int]"
RGBA_Array_Float: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimFloat]]"
RGBA_Tuple_Float: TypeAlias = "tuple[float, float, float, float]"
RGBA_Array_Int: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimInt]]"
RGBA_Tuple_Int: TypeAlias = "tuple[int, int, int, int]"
HSV_Array_Float: TypeAlias = RGB_Array_Float
HSV_Tuple_Float: TypeAlias = RGB_Tuple_Float
ManimColorInternal: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimColorDType]]"
import re
re_hex = re.compile("((?<=#)|(?<=0x))[A-F0-9]{6,8}", re.IGNORECASE)
class ManimColor:
"""Internal representation of a color.
The ManimColor class is the main class for the representation of a color.
It's internal representation is a 4 element array of floats corresponding
to a [r,g,b,a] value where r,g,b,a can be between 0 to 1.
This is done in order to reduce the amount of color inconsitencies by constantly
casting between integers and floats which introduces errors.
The class can accept any value of type :class:`ParsableManimColor` i.e.
ManimColor, int, str, RGB_Tuple_Int, RGB_Tuple_Float, RGBA_Tuple_Int, RGBA_Tuple_Float, RGB_Array_Int,
RGB_Array_Float, RGBA_Array_Int, RGBA_Array_Float
ManimColor itself only accepts singular values and will directly interpret them into a single color if possible
Be careful when passing strings to ManimColor it can create a big overhead for the color processing.
If you want to parse a list of colors use the function :meth:`parse` in :class:`ManimColor` which assumes that
you are going to pass a list of color so arrays will not bei interpreted as a single color.
.. warning::
If you pass an array of numbers to :meth:`parse` it will interpret the r,g,b,a numbers in that array as colors
so instead of the expect singular color you get and array with 4 colors.
For conversion behaviors see the _internal functions for further documentation
Parameters
----------
value
Some representation of a color (e.g., a string or
a suitable tuple).
alpha
The opacity of the color. By default, colors are
fully opaque (value 1.0).
"""
def __init__(
self,
value: ParsableManimColor,
alpha: float = 1.0,
) -> None:
if value is None:
self._internal_value = np.array((0, 0, 0, alpha), dtype=ManimColorDType)
elif isinstance(value, ManimColor):
# logger.info(
# "ManimColor was passed another ManimColor. This is probably not what "
# "you want. Created a copy of the passed ManimColor instead."
# )
self._internal_value = value._internal_value
elif isinstance(value, int):
self._internal_value = ManimColor._internal_from_integer(value, alpha)
elif isinstance(value, str):
result = re_hex.search(value)
if result is not None:
self._internal_value = ManimColor._internal_from_hex_string(
result.group(), alpha
)
else:
# This is not expected to be called on module initialization time
# It can be horribly slow to convert a string to a color because
# it has to access the dictionary of colors and find the right color
self._internal_value = ManimColor._internal_from_string(value)
elif isinstance(value, (list, tuple, np.ndarray)):
length = len(value)
if all(isinstance(x, float) for x in value):
if length == 3:
self._internal_value = ManimColor._internal_from_rgb(value, alpha) # type: ignore
elif length == 4:
self._internal_value = ManimColor._internal_from_rgba(value) # type: ignore
else:
raise ValueError(
f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
)
else:
if length == 3:
self._internal_value = ManimColor._internal_from_int_rgb(
value, alpha # type: ignore
)
elif length == 4:
self._internal_value = ManimColor._internal_from_int_rgba(value) # type: ignore
else:
raise ValueError(
f"ManimColor only accepts lists/tuples/arrays of length 3 or 4, not {length}"
)
elif hasattr(value, "get_hex") and callable(value.get_hex):
result = re_hex.search(value.get_hex())
if result is None:
raise ValueError(f"Failed to parse a color from {value}")
self._internal_value = ManimColor._internal_from_hex_string(
result.group(), alpha
)
else:
# logger.error(f"Invalid color value: {value}")
raise TypeError(
"ManimColor only accepts int, str, list[int, int, int], "
"list[int, int, int, int], list[float, float, float], "
f"list[float, float, float, float], not {type(value)}"
)
@property
def _internal_value(self) -> ManimColorInternal:
"""Returns the internal value of the current Manim color [r,g,b,a] float array
Returns
-------
ManimColorInternal
internal color representation
"""
return self.__value
@_internal_value.setter
def _internal_value(self, value: ManimColorInternal) -> None:
"""Overwrites the internal color value of the ManimColor object
Parameters
----------
value : ManimColorInternal
The value which will overwrite the current color
Raises
------
TypeError
Raises a TypeError if an invalid array is passed
"""
if not isinstance(value, np.ndarray):
raise TypeError("value must be a numpy array")
if value.shape[0] != 4:
raise TypeError("Array must have 4 values exactly")
self.__value: ManimColorInternal = value
@staticmethod
def _internal_from_integer(value: int, alpha: float) -> ManimColorInternal:
return np.asarray(
(
((value >> 16) & 0xFF) / 255,
((value >> 8) & 0xFF) / 255,
((value >> 0) & 0xFF) / 255,
alpha,
),
dtype=ManimColorDType,
)
# TODO: Maybe make 8 nibble hex also convertible ?
@staticmethod
def _internal_from_hex_string(hex: str, alpha: float) -> ManimColorInternal:
"""Internal function for converting a hex string into the internal representation of a ManimColor.
.. warning::
This does not accept any prefixes like # or similar in front of the hex string.
This is just intended for the raw hex part
*For internal use only*
Parameters
----------
hex : str
hex string to be parsed
alpha : float
alpha value used for the color
Returns
-------
ManimColorInternal
Internal color representation
"""
if len(hex) == 6:
hex += "00"
tmp = int(hex, 16)
return np.asarray(
(
((tmp >> 24) & 0xFF) / 255,
((tmp >> 16) & 0xFF) / 255,
((tmp >> 8) & 0xFF) / 255,
alpha,
),
dtype=ManimColorDType,
)
@staticmethod
def _internal_from_int_rgb(
rgb: RGB_Tuple_Int, alpha: float = 1.0
) -> ManimColorInternal:
"""Internal function for converting a rgb tuple of integers into the internal representation of a ManimColor.
*For internal use only*
Parameters
----------
rgb : RGB_Tuple_Int
integer rgb tuple to be parsed
alpha : float, optional
optional alpha value, by default 1.0
Returns
-------
ManimColorInternal
Internal color representation
"""
value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy() / 255
value.resize(4, refcheck=False)
value[3] = alpha
return value
@staticmethod
def _internal_from_rgb(
rgb: RGB_Tuple_Float, alpha: float = 1.0
) -> ManimColorInternal:
"""Internal function for converting a rgb tuple of floats into the internal representation of a ManimColor.
*For internal use only*
Parameters
----------
rgb : RGB_Tuple_Float
float rgb tuple to be parsed
alpha : float, optional
optional alpha value, by default 1.0
Returns
-------
ManimColorInternal
Internal color representation
"""
value: np.ndarray = np.asarray(rgb, dtype=ManimColorDType).copy()
value.resize(4, refcheck=False)
value[3] = alpha
return value
@staticmethod
def _internal_from_int_rgba(rgba: RGBA_Tuple_Int) -> ManimColorInternal:
"""Internal function for converting a rgba tuple of integers into the internal representation of a ManimColor.
*For internal use only*
Parameters
----------
rgba : RGBA_Tuple_Int
int rgba tuple to be parsed
Returns
-------
ManimColorInternal
Internal color representation
"""
return np.asarray(rgba, dtype=ManimColorDType) / 255
@staticmethod
def _internal_from_rgba(rgba: RGBA_Tuple_Float) -> ManimColorInternal:
"""Internal function for converting a rgba tuple of floats into the internal representation of a ManimColor.
*For internal use only*
Parameters
----------
rgba : RGBA_Tuple_Float
int rgba tuple to be parsed
Returns
-------
ManimColorInternal
Internal color representation
"""
return np.asarray(rgba, dtype=ManimColorDType)
@staticmethod
def _internal_from_string(name: str) -> ManimColorInternal:
"""Internal function for converting a string into the internal representation of a ManimColor.
This is not used for hex strings, please refer to :meth:`_internal_from_hex` for this functionality.
*For internal use only*
Parameters
----------
name : str
The color name to be parsed into a color. Refer to the different color Modules in the documentation Page to
find the corresponding Color names.
Returns
-------
ManimColorInternal
Internal color representation
Raises
------
ValueError
Raises a ValueError if the color name is not present with manim
"""
from . import _all_color_dict
upper_name = name.upper()
if upper_name in _all_color_dict:
return _all_color_dict[upper_name]._internal_value
else:
raise ValueError(f"Color {name} not found")
def to_integer(self) -> int:
"""Converts the current ManimColor into an integer
Returns
-------
int
integer representation of the color
.. warning::
This will return only the rgb part of the color
"""
return int.from_bytes(
(self._internal_value[:3] * 255).astype(int).tobytes(), "big"
)
def to_rgb(self) -> RGB_Array_Float:
"""Converts the current ManimColor into a rgb array of floats
Returns
-------
RGB_Array_Float
rgb array with 3 elements of type float
"""
return self._internal_value[:3]
def to_int_rgb(self) -> RGB_Array_Int:
"""Converts the current ManimColor into a rgb array of int
Returns
-------
RGB_Array_Int
rgb array with 3 elements of type int
"""
return (self._internal_value[:3] * 255).astype(int)
def to_rgba(self) -> RGBA_Array_Float:
"""Converts the current ManimColor into a rgba array of floats
Returns
-------
RGBA_Array_Float
rgba array with 4 elements of type float
"""
return self._internal_value
def to_int_rgba(self) -> RGBA_Array_Int:
"""Converts the current ManimColor into a rgba array of int
Returns
-------
RGBA_Array_Int
rgba array with 4 elements of type int
"""
return (self._internal_value * 255).astype(int)
def to_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Float:
"""Converts the current ManimColor into a rgba array of float as :meth:`to_rgba` but you can change the alpha
value.
Parameters
----------
alpha : float
alpha value to be used in the return value
Returns
-------
RGBA_Array_Float
rgba array with 4 elements of type float
"""
return np.fromiter((*self._internal_value[:3], alpha), dtype=ManimColorDType)
def to_int_rgba_with_alpha(self, alpha: float) -> RGBA_Array_Int:
"""Converts the current ManimColor into a rgba array of integers as :meth:`to_int_rgba` but you can change the alpha
value.
Parameters
----------
alpha : float
alpha value to be used for the return value. (Will automatically be scaled from 0-1 to 0-255 so just pass 0-1)
Returns
-------
RGBA_Array_Int
rgba array with 4 elements of type int
"""
tmp = self._internal_value * 255
tmp[3] = alpha * 255
return tmp.astype(int)
def to_hex(self, with_alpha: bool = False) -> str:
"""Converts the manim color to a hexadecimal representation of the color
Parameters
----------
with_alpha : bool, optional
Changes the result from 6 to 8 values where the last 2 nibbles represent the alpha value of 0-255,
by default False
Returns
-------
str
A hex string starting with a # with either 6 or 8 nibbles depending on your input, by default 6 i.e #XXXXXX
"""
tmp = f"#{int(self._internal_value[0]*255):02X}{int(self._internal_value[1]*255):02X}{int(self._internal_value[2]*255):02X}"
if with_alpha:
tmp += f"{int(self._internal_value[3]*255):02X}"
return tmp
def to_hsv(self) -> HSV_Array_Float:
"""Converts the Manim Color to HSV array.
.. note::
Be careful this returns an array in the form `[h, s, v]` where the elements are floats.
This might be confusing because rgb can also be an array of floats so you might want to annotate the usage
of this function in your code by typing the variables with :class:`HSV_Array_Float` in order to differentiate
between rgb arrays and hsv arrays
Returns
-------
HSV_Array_Float
A hsv array containing 3 elements of type float ranging from 0 to 1
"""
return colorsys.rgb_to_hsv(*self.to_rgb())
def invert(self, with_alpha=False) -> ManimColor:
"""Returns an linearly inverted version of the color (no inplace changes)
Parameters
----------
with_alpha : bool, optional
if true the alpha value will be inverted too, by default False
.. note::
This can result in unintended behavior where objects are not displayed because their alpha
value is suddenly 0 or very low. Please keep that in mind when setting this to true
Returns
-------
ManimColor
The linearly inverted ManimColor
"""
return ManimColor(1.0 - self._internal_value, with_alpha)
def interpolate(self, other: ManimColor, alpha: float) -> ManimColor:
"""Interpolates between the current and the given ManimColor an returns the interpolated color
Parameters
----------
other : ManimColor
The other ManimColor to be used for interpolation
alpha : float
A point on the line in rgba colorspace connecting the two colors i.e. the interpolation point
0 corresponds to the current ManimColor and 1 corresponds to the other ManimColor
Returns
-------
ManimColor
The interpolated ManimColor
"""
return ManimColor(
self._internal_value * (1 - alpha) + other._internal_value * alpha
)
@classmethod
def from_rgb(
cls,
rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int,
alpha: float = 1.0,
) -> ManimColor:
"""Creates a ManimColor from an RGB Array. Automagically decides which type it is int/float
.. warning::
Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
being interpreted as if it was a float rgb array with the value 5.0 and not the integer 5
Parameters
----------
rgb : RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int
Any 3 Element Iterable
alpha : float, optional
alpha value to be used in the color, by default 1.0
Returns
-------
ManimColor
Returns the ManimColor object
"""
return cls(rgb, alpha)
@classmethod
def from_rgba(
cls, rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
) -> ManimColor:
"""Creates a ManimColor from an RGBA Array. Automagically decides which type it is int/float
.. warning::
Please make sure that your elements are not floats if you want integers. A 5.0 will result in the input
being interpreted as if it was a float rgb array with the value 5.0 and not the integer 5
Parameters
----------
rgba : RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int
Any 4 Element Iterable
Returns
-------
ManimColor
Returns the ManimColor object
"""
return cls(rgba)
@classmethod
def from_hex(cls, hex: str, alpha: float = 1.0) -> ManimColor:
"""Creates a Manim Color from a hex string, prefixes allowed # and 0x
Parameters
----------
hex : str
The hex string to be converted (currently only supports 6 nibbles)
alpha : float, optional
alpha value to be used for the hex string, by default 1.0
Returns
-------
ManimColor
The ManimColor represented by the hex string
"""
return cls(hex, alpha)
@classmethod
def from_hsv(
cls, hsv: HSV_Array_Float | HSV_Tuple_Float, alpha: float = 1.0
) -> ManimColor:
"""Creates a ManimColor from an HSV Array
Parameters
----------
hsv : HSV_Array_Float | HSV_Tuple_Float
Any 3 Element Iterable containing floats from 0-1
alpha : float, optional
the alpha value to be used, by default 1.0
Returns
-------
ManimColor
The ManimColor with the corresponding RGB values to the HSV
"""
rgb = colorsys.hsv_to_rgb(*hsv)
return cls(rgb, alpha)
@classmethod
def parse(
cls,
color: ParsableManimColor | list[ParsableManimColor] | None,
alpha: float = 1.0,
) -> ManimColor | list[ManimColor]:
"""
Handles the parsing of a list of colors or a single color.
Parameters
----------
color
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.
alpha
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.
Returns
-------
ManimColor
Either a list of colors or a singular color depending on the input
"""
if isinstance(color, (list, tuple)):
return [cls(c, alpha) for c in color] # type: ignore
return cls(color, alpha) # type: ignore
@staticmethod
def gradient(colors: list[ManimColor], length: int):
"""This is not implemented by now refer to :func:`color_gradient` for a working implementation for now"""
# TODO: implement proper gradient, research good implementation for this or look at 3b1b implementation
raise NotImplementedError
def __repr__(self) -> str:
return f"{self.__class__.__name__}('{self.to_hex()}')"
def __str__(self) -> str:
return f"{self.to_hex()}"
def __eq__(self, other: object) -> bool:
if not isinstance(other, ManimColor):
raise TypeError(
f"Cannot compare {self.__class__.__name__} with {other.__class__.__name__}"
)
return np.allclose(self._internal_value, other._internal_value)
def __add__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value + other._internal_value)
def __sub__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value - other._internal_value)
def __mul__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value * other._internal_value)
def __truediv__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value / other._internal_value)
def __floordiv__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value // other._internal_value)
def __mod__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value % other._internal_value)
def __pow__(self, other: ManimColor) -> ManimColor:
return ManimColor(self._internal_value**other._internal_value)
def __and__(self, other: ManimColor) -> ManimColor:
return ManimColor(self.to_integer() & other.to_integer())
def __or__(self, other: ManimColor) -> ManimColor:
return ManimColor(self.to_integer() | other.to_integer())
def __xor__(self, other: ManimColor) -> ManimColor:
return ManimColor(self.to_integer() ^ other.to_integer())
ParsableManimColor: TypeAlias = Union[
ManimColor,
int,
str,
RGB_Tuple_Int,
RGB_Tuple_Float,
RGBA_Tuple_Int,
RGBA_Tuple_Float,
RGB_Array_Int,
RGB_Array_Float,
RGBA_Array_Int,
RGBA_Array_Float,
]
"""ParsableManimColor is the representation for all types that are parsable to a color in manim"""
def color_to_rgb(color: ParsableManimColor) -> RGB_Array_Float:
"""Helper function for use in functional style programming refer to :meth:`to_rgb` in :class:`ManimColor`
Parameters
----------
color : ParsableManimColor
A color
Returns
-------
RGB_Array_Float
the corresponding rgb array
"""
return ManimColor(color).to_rgb()
def color_to_rgba(color: ParsableManimColor, alpha: float = 1) -> RGBA_Array_Float:
"""Helper function for use in functional style programming refer to :meth:`to_rgba_with_alpha` in :class:`ManimColor`
Parameters
----------
color : ParsableManimColor
A color
alpha : float, optional
alpha value to be used in the color, by default 1
Returns
-------
RGBA_Array_Float
the corresponding rgba array
"""
return ManimColor(color).to_rgba_with_alpha(alpha)
def color_to_int_rgb(color: ManimColor) -> RGB_Array_Int:
"""Helper function for use in functional style programming refer to :meth:`to_int_rgb` in :class:`ManimColor`
Parameters
----------
color : ManimColor
A color
Returns
-------
RGB_Array_Int
the corresponding int rgb array
"""
return ManimColor(color).to_int_rgb()
def color_to_int_rgba(color: ManimColor, alpha: float = 1.0) -> RGBA_Array_Int:
"""Helper function for use in functional style programming refer to :meth:`to_int_rgba_with_alpha` in :class:`ManimColor`
Parameters
----------
color : ManimColor
A color
alpha : float, optional
alpha value to be used in the color, by default 1.0
Returns
-------
RGBA_Array_Int
the corresponding int rgba array
"""
return ManimColor(color).to_int_rgba_with_alpha(alpha)
def rgb_to_color(rgb: RGB_Array_Float | RGB_Tuple_Float) -> ManimColor:
"""Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor`
Parameters
----------
rgb : RGB_Array_Float | RGB_Tuple_Float
A 3 element iterable
Returns
-------
ManimColor
A ManimColor with the corresponding value
"""
return ManimColor.from_rgb(rgb)
def rgba_to_color(rgba: RGBA_Array_Float | RGBA_Tuple_Float) -> ManimColor:
"""Helper function for use in functional style programming refer to :meth:`from_rgba` in :class:`ManimColor`
Parameters
----------
rgba : RGBA_Array_Float | RGBA_Tuple_Float
A 4 element iterable
Returns
-------
ManimColor
A ManimColor with the corresponding value
"""
return ManimColor.from_rgba(rgba)
def rgb_to_hex(rgb: RGB_Array_Float | RGB_Tuple_Float) -> str:
"""Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor`
Parameters
----------
rgb : RGB_Array_Float | RGB_Tuple_Float
A 3 element iterable
Returns
-------
str
A hex representation of the color, refer to :meth:`to_hex` in :class:`ManimColor`
"""
return ManimColor.from_rgb(rgb).to_hex()
def hex_to_rgb(hex_code: str) -> RGB_Array_Float:
"""Helper function for use in functional style programming refer to :meth:`to_hex` in :class:`ManimColor`
Parameters
----------
hex_code : str
A hex string representing a color
Returns
-------
RGB_Array_Float
RGB array representing the color
"""
return ManimColor(hex_code).to_rgb()
def invert_color(color: ManimColor) -> ManimColor:
"""Helper function for use in functional style programming refer to :meth:`invert` in :class:`ManimColor`
Parameters
----------
color : ManimColor
A ManimColor
Returns
-------
ManimColor
The linearly inverted ManimColor
"""
return color.invert()
def interpolate_arrays(
arr1: np.ndarray[Any, Any], arr2: np.ndarray[Any, Any], alpha: float
) -> np.ndarray:
"""Helper function used in Manim to fade between two objects smoothly
Parameters
----------
arr1 : np.ndarray[Any, Any]
The first array of colors
arr2 : np.ndarray[Any, Any]
The second array of colors
alpha : float
The alpha value corresponding to the interpolation point between the two inputs
Returns
-------
np.ndarray
The interpolated value of the to arrays
"""
return (1 - alpha) * arr1 + alpha * arr2
def color_gradient(
reference_colors: Sequence[ParsableManimColor],
length_of_output: int,
) -> list[ManimColor] | ManimColor:
"""Creates a list of colors interpolated between the input array of colors with a specific number of colors
Parameters
----------
reference_colors : Sequence[ParsableManimColor]
The colors to be interpolated between or spread apart
length_of_output : int
The number of colors that the output should have, ideally more than the input
Returns
-------
list[ManimColor] | ManimColor
A list of ManimColor's which has the interpolated colors
"""
if length_of_output == 0:
return ManimColor(reference_colors[0])
if len(reference_colors) == 1:
return [ManimColor(reference_colors[0])] * length_of_output
rgbs = list(map(color_to_rgb, reference_colors))
alphas = np.linspace(0, (len(rgbs) - 1), length_of_output)
floors = alphas.astype("int")
alphas_mod1 = alphas % 1
# End edge case
alphas_mod1[-1] = 1
floors[-1] = len(rgbs) - 2
return [
rgb_to_color((rgbs[i] * (1 - alpha)) + (rgbs[i + 1] * alpha))
for i, alpha in zip(floors, alphas_mod1)
]
def interpolate_color(
color1: ManimColor, color2: ManimColor, alpha: float
) -> ManimColor:
"""Standalone function to interpolate two ManimColors and get the result refer to :meth:`interpolate` in :class:`ManimColor`
Parameters
----------
color1 : ManimColor
First ManimColor
color2 : ManimColor
Second ManimColor
alpha : float
The alpha value determining the point of interpolation between the colors
Returns
-------
ManimColor
The interpolated ManimColor
"""
return color1.interpolate(color2, alpha)
def average_color(*colors: ManimColor) -> ManimColor:
"""Determines the Average color of the given parameters
Returns
-------
ManimColor
The average color of the input
"""
rgbs = np.array(list(map(color_to_rgb, colors)))
mean_rgb = np.apply_along_axis(np.mean, 0, rgbs)
return rgb_to_color(mean_rgb)
def random_bright_color() -> ManimColor:
"""Returns you a random bright color
.. warning::
This operation is very expensive please keep in mind the performance loss.
Returns
-------
ManimColor
A bright ManimColor
"""
color = random_color()
curr_rgb = color_to_rgb(color)
new_rgb = interpolate_arrays(curr_rgb, np.ones(len(curr_rgb)), 0.5)
return ManimColor(new_rgb)
def random_color() -> ManimColor:
"""Return you a random ManimColor
.. warning::
This operation is very expensive please keep in mind the performance loss.
Returns
-------
ManimColor
_description_
"""
import manim.utils.color.manim_colors as manim_colors
return random.choice(manim_colors._all_manim_colors)
def get_shaded_rgb(
rgb: np.ndarray,
point: np.ndarray,
unit_normal_vect: np.ndarray,
light_source: np.ndarray,
) -> RGBA_Array_Float:
to_sun = normalize(light_source - point)
factor = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3
if factor < 0:
factor *= 0.5
result = rgb + factor
return result
__all__ = [
"ManimColor",
"ManimColorDType",
"ParsableManimColor",
"color_to_rgb",
"color_to_rgba",
"color_to_int_rgb",
"color_to_int_rgba",
"rgb_to_color",
"rgba_to_color",
"rgb_to_hex",
"hex_to_rgb",
"invert_color",
"interpolate_arrays",
"color_gradient",
"interpolate_color",
"average_color",
"random_bright_color",
"random_color",
"get_shaded_rgb",
]

View file

@ -0,0 +1,220 @@
"""Colors included in the global name space.
These colors form Manim's default color space.
.. manim:: ColorsOverview
:save_last_frame:
:hide_source:
import manim.utils.color.manim_colors as Colors
class ColorsOverview(Scene):
def construct(self):
def color_group(color):
group = VGroup(
*[
Line(ORIGIN, RIGHT * 1.5, stroke_width=35, color=getattr(Colors, name.upper()))
for name in subnames(color)
]
).arrange_submobjects(buff=0.4, direction=DOWN)
name = Text(color).scale(0.6).next_to(group, UP, buff=0.3)
if any(decender in color for decender in "gjpqy"):
name.shift(DOWN * 0.08)
group.add(name)
return group
def subnames(name):
return [name + "_" + char for char in "abcde"]
color_groups = VGroup(
*[
color_group(color)
for color in [
"blue",
"teal",
"green",
"yellow",
"gold",
"red",
"maroon",
"purple",
]
]
).arrange_submobjects(buff=0.2, aligned_edge=DOWN)
for line, char in zip(color_groups[0], "abcde"):
color_groups.add(Text(char).scale(0.6).next_to(line, LEFT, buff=0.2))
def named_lines_group(length, colors, names, text_colors, align_to_block):
lines = VGroup(
*[
Line(
ORIGIN,
RIGHT * length,
stroke_width=55,
color=getattr(Colors, color.upper()),
)
for color in colors
]
).arrange_submobjects(buff=0.6, direction=DOWN)
for line, name, color in zip(lines, names, text_colors):
line.add(Text(name, color=color).scale(0.6).move_to(line))
lines.next_to(color_groups, DOWN, buff=0.5).align_to(
color_groups[align_to_block], LEFT
)
return lines
other_colors = (
"pink",
"light_pink",
"orange",
"light_brown",
"dark_brown",
"gray_brown",
)
other_lines = named_lines_group(
3.2,
other_colors,
other_colors,
[BLACK] * 4 + [WHITE] * 2,
0,
)
gray_lines = named_lines_group(
6.6,
["white"] + subnames("gray") + ["black"],
[
"white",
"lighter_gray / gray_a",
"light_gray / gray_b",
"gray / gray_c",
"dark_gray / gray_d",
"darker_gray / gray_e",
"black",
],
[BLACK] * 3 + [WHITE] * 4,
2,
)
pure_colors = (
"pure_red",
"pure_green",
"pure_blue",
)
pure_lines = named_lines_group(
3.2,
pure_colors,
pure_colors,
[BLACK, BLACK, WHITE],
6,
)
self.add(color_groups, other_lines, gray_lines, pure_lines)
VGroup(*self.mobjects).move_to(ORIGIN)
.. automanimcolormodule:: manim.utils.color.manim_colors
"""
from typing import List
from .core import ManimColor
WHITE: ManimColor = ManimColor("#FFFFFF")
GRAY_A: ManimColor = ManimColor("#DDDDDD")
GREY_A: ManimColor = ManimColor("#DDDDDD")
GRAY_B: ManimColor = ManimColor("#BBBBBB")
GREY_B: ManimColor = ManimColor("#BBBBBB")
GRAY_C: ManimColor = ManimColor("#888888")
GREY_C: ManimColor = ManimColor("#888888")
GRAY_D: ManimColor = ManimColor("#444444")
GREY_D: ManimColor = ManimColor("#444444")
GRAY_E: ManimColor = ManimColor("#222222")
GREY_E: ManimColor = ManimColor("#222222")
BLACK: ManimColor = ManimColor("#000000")
LIGHTER_GRAY: ManimColor = ManimColor("#DDDDDD")
LIGHTER_GREY: ManimColor = ManimColor("#DDDDDD")
LIGHT_GRAY: ManimColor = ManimColor("#BBBBBB")
LIGHT_GREY: ManimColor = ManimColor("#BBBBBB")
GRAY: ManimColor = ManimColor("#888888")
GREY: ManimColor = ManimColor("#888888")
DARK_GRAY: ManimColor = ManimColor("#444444")
DARK_GREY: ManimColor = ManimColor("#444444")
DARKER_GRAY: ManimColor = ManimColor("#222222")
DARKER_GREY: ManimColor = ManimColor("#222222")
BLUE_A: ManimColor = ManimColor("#C7E9F1")
BLUE_B: ManimColor = ManimColor("#9CDCEB")
BLUE_C: ManimColor = ManimColor("#58C4DD")
BLUE_D: ManimColor = ManimColor("#29ABCA")
BLUE_E: ManimColor = ManimColor("#236B8E")
PURE_BLUE: ManimColor = ManimColor("#0000FF")
BLUE: ManimColor = ManimColor("#58C4DD")
DARK_BLUE: ManimColor = ManimColor("#236B8E")
TEAL_A: ManimColor = ManimColor("#ACEAD7")
TEAL_B: ManimColor = ManimColor("#76DDC0")
TEAL_C: ManimColor = ManimColor("#5CD0B3")
TEAL_D: ManimColor = ManimColor("#55C1A7")
TEAL_E: ManimColor = ManimColor("#49A88F")
TEAL: ManimColor = ManimColor("#5CD0B3")
GREEN_A: ManimColor = ManimColor("#C9E2AE")
GREEN_B: ManimColor = ManimColor("#A6CF8C")
GREEN_C: ManimColor = ManimColor("#83C167")
GREEN_D: ManimColor = ManimColor("#77B05D")
GREEN_E: ManimColor = ManimColor("#699C52")
PURE_GREEN: ManimColor = ManimColor("#00FF00")
GREEN: ManimColor = ManimColor("#83C167")
YELLOW_A: ManimColor = ManimColor("#FFF1B6")
YELLOW_B: ManimColor = ManimColor("#FFEA94")
YELLOW_C: ManimColor = ManimColor("#FFFF00")
YELLOW_D: ManimColor = ManimColor("#F4D345")
YELLOW_E: ManimColor = ManimColor("#E8C11C")
YELLOW: ManimColor = ManimColor("#FFFF00")
GOLD_A: ManimColor = ManimColor("#F7C797")
GOLD_B: ManimColor = ManimColor("#F9B775")
GOLD_C: ManimColor = ManimColor("#F0AC5F")
GOLD_D: ManimColor = ManimColor("#E1A158")
GOLD_E: ManimColor = ManimColor("#C78D46")
GOLD: ManimColor = ManimColor("#F0AC5F")
RED_A: ManimColor = ManimColor("#F7A1A3")
RED_B: ManimColor = ManimColor("#FF8080")
RED_C: ManimColor = ManimColor("#FC6255")
RED_D: ManimColor = ManimColor("#E65A4C")
RED_E: ManimColor = ManimColor("#CF5044")
PURE_RED: ManimColor = ManimColor("#FF0000")
RED: ManimColor = ManimColor("#FC6255")
MAROON_A: ManimColor = ManimColor("#ECABC1")
MAROON_B: ManimColor = ManimColor("#EC92AB")
MAROON_C: ManimColor = ManimColor("#C55F73")
MAROON_D: ManimColor = ManimColor("#A24D61")
MAROON_E: ManimColor = ManimColor("#94424F")
MAROON: ManimColor = ManimColor("#C55F73")
PURPLE_A: ManimColor = ManimColor("#CAA3E8")
PURPLE_B: ManimColor = ManimColor("#B189C6")
PURPLE_C: ManimColor = ManimColor("#9A72AC")
PURPLE_D: ManimColor = ManimColor("#715582")
PURPLE_E: ManimColor = ManimColor("#644172")
PURPLE: ManimColor = ManimColor("#9A72AC")
PINK: ManimColor = ManimColor("#D147BD")
LIGHT_PINK: ManimColor = ManimColor("#DC75CD")
ORANGE: ManimColor = ManimColor("#FF862F")
LIGHT_BROWN: ManimColor = ManimColor("#CD853F")
DARK_BROWN: ManimColor = ManimColor("#8B4513")
GRAY_BROWN: ManimColor = ManimColor("#736357")
GREY_BROWN: ManimColor = ManimColor("#736357")
# Colors used for Manim Community's logo and banner
LOGO_WHITE = ManimColor("#ECE7E2")
LOGO_GREEN = ManimColor("#87C2A5")
LOGO_BLUE = ManimColor("#525893")
LOGO_RED = ManimColor("#E07A5F")
LOGO_BLACK = ManimColor("#343434")
_all_manim_colors: List[ManimColor] = [
x for x in globals().values() if isinstance(x, ManimColor)
]

View file

@ -0,0 +1,92 @@
from __future__ import annotations
import inspect
from docutils import nodes
from docutils.parsers.rst import Directive
from sphinx.application import Sphinx
from manim import ManimColor
def setup(app: Sphinx) -> None:
app.add_directive("automanimcolormodule", ManimColorModuleDocumenter)
class ManimColorModuleDocumenter(Directive):
objtype = "automanimcolormodule"
required_arguments = 1
has_content = True
def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig)
def run(
self,
) -> None:
module_name = self.arguments[0]
try:
import importlib
module = importlib.import_module(module_name)
except ImportError:
return [
nodes.error(
None,
nodes.paragraph(text="Failed to import module '%s'" % module_name),
)
]
# Number of Colors displayed in one row
num_color_cols = 2
table = nodes.table(align="center")
tgroup = nodes.tgroup(cols=num_color_cols * 2)
table += tgroup
for _ in range(num_color_cols * 2):
tgroup += nodes.colspec(colwidth=1)
# Create header rows for the table
thead = nodes.thead()
row = nodes.row()
for _ in range(num_color_cols):
col1 = nodes.paragraph(text="Color Name")
col2 = nodes.paragraph(text="RGB Hex Code")
row += nodes.entry("", col1)
row += nodes.entry("", col2)
thead += row
tgroup += thead
color_elements = []
for member_name, member_obj in inspect.getmembers(module):
if isinstance(member_obj, ManimColor):
r, g, b = member_obj.to_rgb()
luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
# Choose the font color based on the background luminance
if luminance > 0.5:
font_color = "black"
else:
font_color = "white"
color_elements.append((member_name, member_obj.to_hex(), font_color))
tbody = nodes.tbody()
for base_i in range(0, len(color_elements), num_color_cols):
row = nodes.row()
for member_name, hex_code, font_color in color_elements[
base_i : base_i + num_color_cols
]:
col1 = nodes.literal(text=member_name)
col2 = nodes.raw(
"",
f'<div style="background-color:{hex_code};padding: 0.25rem 0;border-radius:8px;margin: 0.5rem 0.2rem"><code style="color:{font_color};">{hex_code}</code></div>',
format="html",
)
row += nodes.entry("", col1)
row += nodes.entry("", col2)
tbody += row
tgroup += tbody
return [table]

View file

@ -26,7 +26,7 @@ render scenes that are defined within doctests, for example::
>>> from manim import Create, Dot, RED, Scene
>>> dot = Dot(color=RED)
>>> dot.color
<Color #fc6255>
ManimColor('#FC6255')
>>> class DirectiveDoctestExample(Scene):
... def construct(self):
... self.play(Create(dot))
@ -272,10 +272,13 @@ class ManimDirective(Directive):
f"{clsname}().render()",
]
with tempconfig(example_config):
run_time = timeit(lambda: exec("\n".join(code), globals()), number=1)
video_dir = config.get_dir("video_dir")
images_dir = config.get_dir("images_dir")
try:
with tempconfig(example_config):
run_time = timeit(lambda: exec("\n".join(code), globals()), number=1)
video_dir = config.get_dir("video_dir")
images_dir = config.get_dir("images_dir")
except Exception as e:
raise RuntimeError(f"Error while rendering example {clsname}") from e
_write_rendering_stats(
clsname,

View file

@ -186,7 +186,7 @@ def modify_atime(file_path: str) -> None:
file_path
The path of the file.
"""
os.utime(file_path, times=(time.time(), os.path.getmtime(file_path)))
os.utime(file_path, times=(time.time(), Path(file_path).stat().st_mtime))
def open_file(file_path, in_browser=False):

View file

@ -207,7 +207,7 @@ class _CustomEncoder(json.JSONEncoder):
del cvardict[i]
try:
code = inspect.getsource(obj)
except OSError:
except (OSError, TypeError):
# This happens when rendering videos included in the documentation
# within doctests and should be replaced by a solution avoiding
# hash collision (due to the same, empty, code strings) at some point.

View file

@ -65,7 +65,7 @@ def all_elements_are_instances(iterable: Iterable, Class) -> bool:
"""Returns ``True`` if all elements of iterable are instances of Class.
False otherwise.
"""
return all([isinstance(e, Class) for e in iterable])
return all(isinstance(e, Class) for e in iterable)
def batch_by_property(

View file

@ -44,7 +44,6 @@ import numpy as np
from mapbox_earcut import triangulate_float32 as earcut
from scipy.spatial.transform import Rotation
from .. import config
from ..constants import DOWN, OUT, PI, RIGHT, TAU, UP, RendererType
from ..utils.iterables import adjacent_pairs
@ -332,7 +331,7 @@ def angle_of_vector(vector: Sequence[float] | np.ndarray) -> float:
return np.angle(complex(*vector[:2]))
def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> np.ndarray:
def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> float:
"""Returns the angle between two vectors.
This angle will always be between 0 and pi
@ -345,7 +344,7 @@ def angle_between_vectors(v1: np.ndarray, v2: np.ndarray) -> np.ndarray:
Returns
-------
np.ndarray
float
The angle between the vectors.
"""

View file

@ -139,6 +139,7 @@ class TexTemplate:
else:
self.preamble += "\n" + txt
self._rebuild()
return self
def add_to_document(self, txt: str):
"""Adds txt to the TeX template just after \\begin{document}, e.g. ``\\boldmath``
@ -150,6 +151,7 @@ class TexTemplate:
"""
self.post_doc_commands += "\n" + txt + "\n"
self._rebuild()
return self
def get_texcode_for_expression(self, expression: str):
"""Inserts expression verbatim into TeX template.

View file

@ -244,7 +244,8 @@ def convert_to_svg(dvi_file: Path, extension: str, page: int = 1):
f"Your installation does not support converting {dvi_file.suffix} files to SVG."
f" Consider updating dvisvgm to at least version 2.4."
f" If this does not solve the problem, please refer to our troubleshooting guide at:"
f" https://docs.manim.community/en/stable/installation/troubleshooting.html",
f" https://docs.manim.community/en/stable/faq/general.html#my-installation-"
f"does-not-support-converting-pdf-to-svg-help",
)
return result

3164
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "manim"
version = "0.17.2"
version = "0.17.3"
description = "Animation engine for explanatory math videos."
authors = ["The Manim Community Developers <contact@manim.community>", "3b1b <grant@3blue1brown.com>"]
license="MIT"
@ -14,10 +14,10 @@ classifiers= [
"Topic :: Scientific/Engineering",
"Topic :: Multimedia :: Video",
"Topic :: Multimedia :: Graphics",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Natural Language :: English",
]
exclude = ["scripts/","logo/","readme-assets/"]
@ -29,7 +29,6 @@ packages = [
python = ">=3.8,<3.12"
click = ">=7.2,<=9.0"
click-default-group = "^1.2.2"
colour = "^0.1.5"
numpy = "^1.19"
Pillow = ">=9.1,<10.0"
scipy = "^1.7.3"
@ -57,8 +56,12 @@ screeninfo = "^0.8"
Pygments = "^2.10.0"
"backports.cached-property" = { version = "^1.0.1", python = "<3.8" }
svgelements = "^1.8.0"
<<<<<<< HEAD
ipython = "^8.7.0"
pyopengl = "^3.1.6"
=======
typing-extensions = "^4.7.1"
>>>>>>> 50d663eb8b3b3ddb43fb20cce1943348abf9cc59
[tool.poetry.extras]
jupyterlab = ["jupyterlab", "notebook"]
@ -70,7 +73,7 @@ pytest = "^7.2"
pylint = "^2.12.2"
Sphinx = "^4"
sphinx-copybutton = "^0.4.0"
sphinxext-opengraph = "^0.5.1"
sphinxext-opengraph = "^0.8"
furo = "^2022.06.21"
recommonmark = "^0.7.1"
matplotlib = "^3.3.2"

View file

@ -183,7 +183,6 @@ def get_summary(body):
try:
has_changelog_pattern = re.search(pattern, body)
if has_changelog_pattern:
return has_changelog_pattern.group()[22:-21].strip()
except Exception:
print(f"Error parsing body for changelog: {body}")

View file

@ -1,3 +1,4 @@
import pathlib
import sys
import numpy as np
@ -9,7 +10,7 @@ def main():
print_usage()
sys.exit(1)
npz_file = sys.argv[1]
output_folder = Path(sys.argv[2])
output_folder = pathlib.Path(sys.argv[2])
if not output_folder.exists():
output_folder.mkdir(parents=True)

View file

@ -1,13 +1,15 @@
from __future__ import annotations
import shutil
import sys
from pathlib import Path
from textwrap import dedent
from click.testing import CliRunner
from manim import __version__, capture
from manim import __version__, capture, tempconfig
from manim.__main__ import main
from manim.cli.checkhealth.checks import HEALTH_CHECKS
def test_manim_version():
@ -62,6 +64,28 @@ Made with <3 by Manim Community developers.
assert dedent(expected_output) == result.output
def test_manim_checkhealth_subcommand():
command = ["checkhealth"]
runner = CliRunner()
result = runner.invoke(main, command)
output_lines = result.output.split("\n")
num_passed = len([line for line in output_lines if "PASSED" in line])
assert num_passed == len(
HEALTH_CHECKS
), f"Some checks failed! Full output:\n{result.output}"
assert "No problems detected, your installation seems healthy!" in output_lines
def test_manim_checkhealth_failing_subcommand():
command = ["checkhealth"]
runner = CliRunner()
with tempconfig({"ffmpeg_executable": "/path/to/nowhere"}):
result = runner.invoke(main, command)
output_lines = result.output.split("\n")
assert "- Checking whether ffmpeg is available ... FAILED" in output_lines
assert "- Checking whether ffmpeg is working ... SKIPPED" in output_lines
def test_manim_init_subcommand():
command = ["init"]
runner = CliRunner()

View file

@ -63,7 +63,6 @@ def test_NumberPlane():
]
for test_data in testing_data:
x_range, y_range = test_data
x_start, x_end = x_range

View file

@ -40,7 +40,11 @@ def test_family():
def test_overlapping_family():
"""Check that each member of the family is only gathered once."""
mob, child1, child2, = (
(
mob,
child1,
child2,
) = (
Mobject(),
Mobject(),
Mobject(),
@ -64,7 +68,11 @@ def test_shift_family():
"""
# Note shift() needs the mobject to have a non-empty `points` attribute, so
# we cannot use a plain Mobject or VMobject. We use Circle instead.
mob, child1, child2, = (
(
mob,
child1,
child2,
) = (
Circle(),
Circle(),
Circle(),

View file

@ -1,8 +1,9 @@
from __future__ import annotations
import numpy as np
import pytest
from manim import Mobject
from manim import DL, UR, Circle, Mobject, Rectangle, Square, VGroup
def test_mobject_add():
@ -49,3 +50,110 @@ def test_mobject_remove():
assert len(obj.submobjects) == 10
assert obj.remove(Mobject()) is obj
def test_mobject_dimensions_single_mobject():
# A Mobject with no points and no submobjects has no dimensions
empty = Mobject()
assert empty.width == 0
assert empty.height == 0
assert empty.depth == 0
has_points = Mobject()
has_points.points = np.array([[-1, -2, -3], [1, 3, 5]])
assert has_points.width == 2
assert has_points.height == 5
assert has_points.depth == 8
rect = Rectangle(width=3, height=5)
assert rect.width == 3
assert rect.height == 5
assert rect.depth == 0
# Dimensions should be recalculated after scaling
rect.scale(2.0)
assert rect.width == 6
assert rect.height == 10
assert rect.depth == 0
# Dimensions should not be dependent on location
rect.move_to([-3, -4, -5])
assert rect.width == 6
assert rect.height == 10
assert rect.depth == 0
circ = Circle(radius=2)
assert circ.width == 4
assert circ.height == 4
assert circ.depth == 0
def is_close(x, y):
return abs(x - y) < 0.00001
def test_mobject_dimensions_nested_mobjects():
vg = VGroup()
for x in range(-5, 8, 1):
row = VGroup()
vg += row
for y in range(-17, 2, 1):
for z in range(0, 10, 1):
s = Square().move_to([x, y, z / 10])
row += s
assert vg.width == 14.0, vg.width
assert vg.height == 20.0, vg.height
assert is_close(vg.depth, 0.9), vg.depth
# Dimensions should be recalculated after scaling
vg.scale(0.5)
assert vg.width == 7.0, vg.width
assert vg.height == 10.0, vg.height
assert is_close(vg.depth, 0.45), vg.depth
# Adding a mobject changes the bounds/dimensions
rect = Rectangle(width=3, height=5)
rect.move_to([9, 3, 1])
vg += rect
assert vg.width == 13.0, vg.width
assert is_close(vg.height, 18.5), vg.height
assert is_close(vg.depth, 0.775), vg.depth
def test_mobject_dimensions_mobjects_with_no_points_are_at_origin():
rect = Rectangle(width=2, height=3)
rect.move_to([-4, -5, 0])
outer_group = VGroup(rect)
# This is as one would expect
assert outer_group.width == 2
assert outer_group.height == 3
# Adding a mobject with no points has a quirk of adding a "point"
# to [0, 0, 0] (the origin). This changes the size of the outer
# group because now the bottom left corner is at [-5, -6.5, 0]
# but the upper right corner is [0, 0, 0] instead of [-3, -3.5, 0]
outer_group.add(VGroup())
assert outer_group.width == 5
assert outer_group.height == 6.5
def test_mobject_dimensions_has_points_and_children():
outer_rect = Rectangle(width=3, height=6)
inner_rect = Rectangle(width=2, height=1)
inner_rect.align_to(outer_rect.get_corner(UR), DL)
outer_rect.add(inner_rect)
# The width of a mobject should depend both on its points and
# the points of all children mobjects.
assert outer_rect.width == 5 # 3 from outer_rect, 2 from inner_rect
assert outer_rect.height == 7 # 6 from outer_rect, 1 from inner_rect
assert outer_rect.depth == 0
assert inner_rect.width == 2
assert inner_rect.height == 1
assert inner_rect.depth == 0

View file

@ -1,7 +1,5 @@
from __future__ import annotations
from colour import Color
from manim import *
from tests.helpers.path_utils import get_svg_resource
@ -9,21 +7,21 @@ from tests.helpers.path_utils import get_svg_resource
def test_set_fill_color():
expected_color = "#FF862F"
svg = SVGMobject(get_svg_resource("heart.svg"), fill_color=expected_color)
assert svg.fill_color == Color(expected_color)
assert svg.fill_color.to_hex() == expected_color
def test_set_stroke_color():
expected_color = "#FFFDDD"
svg = SVGMobject(get_svg_resource("heart.svg"), stroke_color=expected_color)
assert svg.stroke_color == Color(expected_color)
assert svg.stroke_color.to_hex() == expected_color
def test_set_color_sets_fill_and_stroke():
expected_color = "#EEE777"
svg = SVGMobject(get_svg_resource("heart.svg"), color=expected_color)
assert svg.color == Color(expected_color)
assert svg.fill_color == Color(expected_color)
assert svg.stroke_color == Color(expected_color)
assert svg.color.to_hex() == expected_color
assert svg.fill_color.to_hex() == expected_color
assert svg.stroke_color.to_hex() == expected_color
def test_set_fill_opacity():
@ -45,7 +43,7 @@ def test_fill_overrides_color():
color="#123123",
fill_color=expected_color,
)
assert svg.fill_color == Color(expected_color)
assert svg.fill_color.to_hex() == expected_color
def test_stroke_overrides_color():
@ -55,4 +53,84 @@ def test_stroke_overrides_color():
color="#334433",
stroke_color=expected_color,
)
assert svg.stroke_color == Color(expected_color)
assert svg.stroke_color.to_hex() == expected_color
def test_single_path_turns_into_sequence_of_points():
svg = SVGMobject(
get_svg_resource("cubic_and_lineto.svg"),
)
assert len(svg.points) == 0, svg.points
assert len(svg.submobjects) == 1, svg.submobjects
path = svg.submobjects[0]
np.testing.assert_almost_equal(
path.points,
np.array(
[
[-0.166666666666666, 0.66666666666666, 0.0],
[-0.166666666666666, 0.0, 0.0],
[0.5, 0.66666666666666, 0.0],
[0.5, 0.0, 0.0],
[0.5, 0.0, 0.0],
[-0.16666666666666666, 0.0, 0.0],
[0.5, -0.6666666666666666, 0.0],
[-0.166666666666666, -0.66666666666666, 0.0],
[-0.166666666666666, -0.66666666666666, 0.0],
[-0.27777777777777, -0.77777777777777, 0.0],
[-0.38888888888888, -0.88888888888888, 0.0],
[-0.5, -1.0, 0.0],
[-0.5, -1.0, 0.0],
[-0.5, -0.333333333333, 0.0],
[-0.5, 0.3333333333333, 0.0],
[-0.5, 1.0, 0.0],
[-0.5, 1.0, 0.0],
[-0.38888888888888, 0.8888888888888, 0.0],
[-0.27777777777777, 0.7777777777777, 0.0],
[-0.16666666666666, 0.6666666666666, 0.0],
]
),
decimal=5,
)
def test_closed_path_does_not_have_extra_point():
# This dash.svg is the output of a "-" as generated from LaTex.
# It ends back where it starts, so we shouldn't see a final line.
svg = SVGMobject(
get_svg_resource("dash.svg"),
)
assert len(svg.points) == 0, svg.points
assert len(svg.submobjects) == 1, svg.submobjects
dash = svg.submobjects[0]
np.testing.assert_almost_equal(
dash.points,
np.array(
[
[13.524988331417841, -1.0, 0],
[14.374988080480586, -1.0, 0],
[15.274984567359079, -1.0, 0],
[15.274984567359079, 0.0, 0.0],
[15.274984567359079, 0.0, 0.0],
[15.274984567359079, 1.0, 0.0],
[14.374988080480586, 1.0, 0.0],
[13.524988331417841, 1.0, 0.0],
[13.524988331417841, 1.0, 0.0],
[4.508331116720995, 1.0, 0],
[-4.508326097975995, 1.0, 0.0],
[-13.524983312672841, 1.0, 0.0],
[-13.524983312672841, 1.0, 0.0],
[-14.374983061735586, 1.0, 0.0],
[-15.274984567359079, 1.0, 0.0],
[-15.274984567359079, 0.0, 0.0],
[-15.274984567359079, 0.0, 0.0],
[-15.274984567359079, -1.0, 0],
[-14.374983061735586, -1.0, 0],
[-13.524983312672841, -1.0, 0],
[-13.524983312672841, -1.0, 0],
[-4.508326097975995, -1.0, 0],
[4.508331116720995, -1.0, 0],
[13.524988331417841, -1.0, 0],
]
),
decimal=5,
)

View file

@ -1,6 +1,6 @@
from __future__ import annotations
from manim import Graph, Scene, Text, tempconfig
from manim import DiGraph, Graph, Scene, Text, tempconfig
def test_graph_creation():
@ -8,17 +8,19 @@ def test_graph_creation():
edges = [(1, 2), (2, 3), (3, 4), (4, 1)]
layout = {1: [0, 0, 0], 2: [1, 1, 0], 3: [1, -1, 0], 4: [-1, 0, 0]}
G_manual = Graph(vertices=vertices, edges=edges, layout=layout)
assert str(G_manual) == "Graph on 4 vertices and 4 edges"
assert str(G_manual) == "Undirected graph on 4 vertices and 4 edges"
G_spring = Graph(vertices=vertices, edges=edges)
assert str(G_spring) == "Graph on 4 vertices and 4 edges"
assert str(G_spring) == "Undirected graph on 4 vertices and 4 edges"
G_directed = DiGraph(vertices=vertices, edges=edges)
assert str(G_directed) == "Directed graph on 4 vertices and 4 edges"
def test_graph_add_vertices():
G = Graph([1, 2, 3], [(1, 2), (2, 3)])
G.add_vertices(4)
assert str(G) == "Graph on 4 vertices and 2 edges"
assert str(G) == "Undirected graph on 4 vertices and 2 edges"
G.add_vertices(5, labels={5: Text("5")})
assert str(G) == "Graph on 5 vertices and 2 edges"
assert str(G) == "Undirected graph on 5 vertices and 2 edges"
assert 5 in G._labels
assert 5 in G._vertex_config
G.add_vertices(6, 7, 8)
@ -30,12 +32,12 @@ def test_graph_remove_vertices():
G = Graph([1, 2, 3, 4, 5], [(1, 2), (2, 3), (3, 4), (4, 5)])
removed_mobjects = G.remove_vertices(3)
assert len(removed_mobjects) == 3
assert str(G) == "Graph on 4 vertices and 2 edges"
assert str(G) == "Undirected graph on 4 vertices and 2 edges"
assert list(G.vertices.keys()) == [1, 2, 4, 5]
assert list(G.edges.keys()) == [(1, 2), (4, 5)]
removed_mobjects = G.remove_vertices(4, 5)
assert len(removed_mobjects) == 3
assert str(G) == "Graph on 2 vertices and 1 edges"
assert str(G) == "Undirected graph on 2 vertices and 1 edges"
assert list(G.vertices.keys()) == [1, 2]
assert list(G.edges.keys()) == [(1, 2)]
@ -44,19 +46,19 @@ def test_graph_add_edges():
G = Graph([1, 2, 3, 4, 5], [(1, 2), (2, 3)])
added_mobjects = G.add_edges((1, 3))
assert str(added_mobjects.submobjects) == "[Line]"
assert str(G) == "Graph on 5 vertices and 3 edges"
assert str(G) == "Undirected graph on 5 vertices and 3 edges"
assert set(G.vertices.keys()) == {1, 2, 3, 4, 5}
assert set(G.edges.keys()) == {(1, 2), (2, 3), (1, 3)}
added_mobjects = G.add_edges((1, 42))
assert str(added_mobjects.submobjects) == "[Dot, Line]"
assert str(G) == "Graph on 6 vertices and 4 edges"
assert str(G) == "Undirected graph on 6 vertices and 4 edges"
assert set(G.vertices.keys()) == {1, 2, 3, 4, 5, 42}
assert set(G.edges.keys()) == {(1, 2), (2, 3), (1, 3), (1, 42)}
added_mobjects = G.add_edges((4, 5), (5, 6), (6, 7))
assert len(added_mobjects) == 5
assert str(G) == "Graph on 8 vertices and 7 edges"
assert str(G) == "Undirected graph on 8 vertices and 7 edges"
assert set(G.vertices.keys()) == {1, 2, 3, 4, 5, 42, 6, 7}
assert set(G._graph.nodes()) == set(G.vertices.keys())
assert set(G.edges.keys()) == {
@ -75,13 +77,13 @@ def test_graph_remove_edges():
G = Graph([1, 2, 3, 4, 5], [(1, 2), (2, 3), (3, 4), (4, 5), (1, 5)])
removed_mobjects = G.remove_edges((1, 2))
assert str(removed_mobjects.submobjects) == "[Line]"
assert str(G) == "Graph on 5 vertices and 4 edges"
assert str(G) == "Undirected graph on 5 vertices and 4 edges"
assert set(G.edges.keys()) == {(2, 3), (3, 4), (4, 5), (1, 5)}
assert set(G._graph.edges()) == set(G.edges.keys())
removed_mobjects = G.remove_edges((2, 3), (3, 4), (4, 5), (5, 1))
removed_mobjects = G.remove_edges((2, 3), (3, 4), (4, 5), (1, 5))
assert len(removed_mobjects) == 4
assert str(G) == "Graph on 5 vertices and 0 edges"
assert str(G) == "Undirected graph on 5 vertices and 0 edges"
assert set(G._graph.edges()) == set()
assert set(G.edges.keys()) == set()
@ -93,8 +95,8 @@ def test_custom_animation_mobject_list():
assert scene.mobjects == [G]
with tempconfig({"dry_run": True, "quality": "low_quality"}):
scene.play(G.animate.add_vertices(4))
assert str(G) == "Graph on 4 vertices and 2 edges"
assert str(G) == "Undirected graph on 4 vertices and 2 edges"
assert scene.mobjects == [G]
scene.play(G.animate.remove_vertices(2))
assert str(G) == "Graph on 3 vertices and 0 edges"
assert str(G) == "Undirected graph on 3 vertices and 0 edges"
assert scene.mobjects == [G]

Some files were not shown because too many files have changed in this diff Show more