mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Merge branch 'main' of github.com:ManimCommunity/manim into experimental
This commit is contained in:
commit
520577dde3
131 changed files with 8459 additions and 2956 deletions
2
.github/ISSUE_TEMPLATE/installation_issue.md
vendored
2
.github/ISSUE_TEMPLATE/installation_issue.md
vendored
|
|
@ -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
|
||||
|
|
|
|||
61
.github/workflows/ci.yml
vendored
61
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
4
.github/workflows/publish-docker.yml
vendored
4
.github/workflows/publish-docker.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
...
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
BIN
docs/source/_static/logo.png
Normal file
BIN
docs/source/_static/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -7,6 +7,8 @@ Qualified name: ``{{ fullname | escape }}``
|
|||
.. autoclass:: {{ objname }}
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
{% block methods %}
|
||||
{%- if methods %}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
207
docs/source/changelog/0.17.3-changelog.rst
Normal file
207
docs/source/changelog/0.17.3-changelog.rst
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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".
|
||||
|
|
|
|||
|
|
@ -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::
|
||||
|
||||
|
|
|
|||
|
|
@ -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.7–3.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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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.7–3.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.7–3.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.7–3.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.
|
||||
|
|
|
|||
|
|
@ -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 *
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
):
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 [
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ class TransformMatchingAbstractBase(AnimationGroup):
|
|||
key_map: dict | None = None,
|
||||
**kwargs,
|
||||
):
|
||||
|
||||
if isinstance(mobject, OpenGLVMobject):
|
||||
group_type = OpenGLVGroup
|
||||
elif isinstance(mobject, OpenGLMobject):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
0
manim/cli/checkhealth/__init__.py
Normal file
0
manim/cli/checkhealth/__init__.py
Normal file
173
manim/cli/checkhealth/checks.py
Normal file
173
manim/cli/checkhealth/checks.py
Normal 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)
|
||||
81
manim/cli/checkhealth/commands.py
Normal file
81
manim/cli/checkhealth/commands.py
Normal 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()
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ Modules
|
|||
|
||||
~arc
|
||||
~boolean_ops
|
||||
~labeled
|
||||
~line
|
||||
~polygram
|
||||
~shape_matchers
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
155
manim/mobject/geometry/labeled.py
Normal file
155
manim/mobject/geometry/labeled.py
Normal 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)
|
||||
|
|
@ -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,
|
||||
):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"]])
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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__(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
--------
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
"""
|
||||
plugins_flags.py
|
||||
------------
|
||||
|
||||
Plugin Managing Utility.
|
||||
"""
|
||||
"""Plugin Managing Utility"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
234
manim/utils/color/AS2700.py
Normal 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
315
manim/utils/color/BS381.py
Normal 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
530
manim/utils/color/X11.py
Normal 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
949
manim/utils/color/XKCD.py
Normal 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")
|
||||
58
manim/utils/color/__init__.py
Normal file
58
manim/utils/color/__init__.py
Normal 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
998
manim/utils/color/core.py
Normal 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",
|
||||
]
|
||||
220
manim/utils/color/manim_colors.py
Normal file
220
manim/utils/color/manim_colors.py
Normal 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)
|
||||
]
|
||||
92
manim/utils/docbuild/autocolor_directive.py
Normal file
92
manim/utils/docbuild/autocolor_directive.py
Normal 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]
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
3164
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue