diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 22f2974e..5e81dd94 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -19,12 +19,12 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- pip install setuptools wheel twine
+ pip install setuptools wheel twine build
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
- python setup.py sdist bdist_wheel
+ python -m build
twine upload dist/*
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..b97adbb2
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+graft manimlib
+recursive-exclude manimlib *.pyc *.DS_Store
\ No newline at end of file
diff --git a/README.md b/README.md
index 94655b90..4c6afec9 100644
--- a/README.md
+++ b/README.md
@@ -7,16 +7,18 @@
[](https://pypi.org/project/manimgl/)
[](http://choosealicense.com/licenses/mit/)
[](https://www.reddit.com/r/manim/)
-[](https://discord.gg/mMRrZQW)
+[](https://discord.com/invite/bYCyhM9Kz2)
[](https://3b1b.github.io/manim/)
Manim is an engine for precise programmatic animations, designed for creating explanatory math videos.
-Note, there are two versions of manim. This repository began as a personal project by the author of [3Blue1Brown](https://www.3blue1brown.com/) for the purpose of animating those videos, with video-specific code available [here](https://github.com/3b1b/videos). In 2020 a group of developers forked it into what is now the [community edition](https://github.com/ManimCommunity/manim/), with a goal of being more stable, better tested, quicker to respond to community contributions, and all around friendlier to get started with. You can engage with that community by joining the discord.
-
-Since the fork, this version has evolved to work on top of OpenGL, and allows real-time rendering to an interactive window before scenes are finalized and written to a file.
+Note, there are two versions of manim. This repository began as a personal project by the author of [3Blue1Brown](https://www.3blue1brown.com/) for the purpose of animating those videos, with video-specific code available [here](https://github.com/3b1b/videos). In 2020 a group of developers forked it into what is now the [community edition](https://github.com/ManimCommunity/manim/), with a goal of being more stable, better tested, quicker to respond to community contributions, and all around friendlier to get started with. See [this page](https://docs.manim.community/en/stable/installation/versions.html?highlight=OpenGL#which-version-to-use) for more details.
## Installation
+> **WARNING:** These instructions are for ManimGL _only_. Trying to use these instructions to install [ManimCommunity/manim](https://github.com/ManimCommunity/manim) or instructions there to install this version will cause problems. You should first decide which version you wish to install, then only follow the instructions for your desired version.
+>
+> **Note**: To install manim directly through pip, please pay attention to the name of the installed package. This repository is ManimGL of 3b1b. The package name is `manimgl` instead of `manim` or `manimlib`. Please use `pip install manimgl` to install the version in this repository.
+
Manim runs on Python 3.6 or higher (Python 3.8 is recommended).
System requirements are [FFmpeg](https://ffmpeg.org/), [OpenGL](https://www.opengl.org/) and [LaTeX](https://www.latex-project.org) (optional, if you want to use LaTeX).
@@ -102,7 +104,7 @@ Take a look at custom_config.yml for further configuration. To add your customi
Look through the [example scenes](https://3b1b.github.io/manim/getting_started/example_scenes.html) to get a sense of how it is used, and feel free to look through the code behind [3blue1brown videos](https://github.com/3b1b/videos) for a much larger set of example. Note, however, that developments are often made to the library without considering backwards compatibility with those old videos. To run an old project with a guarantee that it will work, you will have to go back to the commit which completed that project.
### Documentation
-Documentation is in progress at [3b1b.github.io/manim](https://3b1b.github.io/manim/). And there is also a Chinese version maintained by **@manim-kindergarten**: [manim.ml](https://manim.ml/) (in Chinese).
+Documentation is in progress at [3b1b.github.io/manim](https://3b1b.github.io/manim/). And there is also a Chinese version maintained by [**@manim-kindergarten**](https://manim.org.cn): [docs.manim.org.cn](https://docs.manim.org.cn/) (in Chinese).
[manim-kindergarten](https://github.com/manim-kindergarten/) wrote and collected some useful extra classes and some codes of videos in [manim_sandbox repo](https://github.com/manim-kindergarten/manim_sandbox).
diff --git a/docs/source/_static/colors.css b/docs/source/_static/colors.css
deleted file mode 100644
index c5b69708..00000000
--- a/docs/source/_static/colors.css
+++ /dev/null
@@ -1,293 +0,0 @@
-p.color-text {
- font-size: inherit;
- font-family: var(--font-stack--monospace);
- margin-top: 25px;
- color: WHITE;
-}
-
-p.color-text-small {
- font-size: small;
- font-family: var(--font-stack--monospace);
- margin-top: 28px;
- color: WHITE;
-}
-
-.colors {
- float: left;
- padding: 10px;
- border: 10px;
- margin: 0;
- width: 80px;
- height: 80px;
- text-align: center;
-}
-
-.BLUE_A {
- background: #C7E9F1;
- color:#C7E9F1;
-}
-
-.BLUE_B {
- background: #9CDCEB;
- color:#9CDCEB;
-}
-
-.BLUE_C {
- background: #58C4DD;
- color:#58C4DD;
-}
-
-.BLUE_D {
- background: #29ABCA;
- color:#29ABCA;
-}
-
-.BLUE_E {
- background: #1C758A;
- color:#1C758A;
-}
-
-.TEAL_A {
- background: #ACEAD7;
- color:#ACEAD7 ;
-}
-
-.TEAL_B {
- background: #76DDC0;
- color: #76DDC0;
-}
-
-.TEAL_C {
- background: #5CD0B3;
- color: #5CD0B3;
-}
-
-.TEAL_D {
- background: #55C1A7;
- color: #55C1A7;
-}
-
-.TEAL_E {
- background: #49A88F;
- color: #49A88F;
-}
-
-.GREEN_A {
- background: #C9E2AE;
- color: #C9E2AE;
-}
-
-.GREEN_B {
- background: #A6CF8C;
- color: #A6CF8C;
-}
-
-.GREEN_C {
- background: #83C167;
- color: #83C167;
-}
-
-.GREEN_D {
- background: #77B05D;
- color: #77B05D;
-}
-
-.GREEN_E {
- background: #699C52;
- color: #699C52;
-}
-
-.YELLOW_A {
- background: #FFF1B6;
- color: #FFF1B6;
-}
-
-.YELLOW_B {
- background: #FFEA94;
- color:#FFEA94 ;
-}
-
-.YELLOW_C {
- background: #FFFF00;
- color: #FFFF00;
-}
-
-.YELLOW_D {
- background: #F4D345;
- color: #F4D345;
-}
-
-.YELLOW_E {
- background: #E8C11C;
- color: #E8C11C;
-}
-
-.GOLD_A {
- background: #F7C797;
- color:#F7C797;
-}
-
-.GOLD_B {
- background: #F9B775;
- color:#F9B775;
-}
-
-.GOLD_C {
- background: #F0AC5F;
- color:#F0AC5F;
-}
-
-.GOLD_D {
- background: #E1A158;
- color:#E1A158;
-}
-
-.GOLD_E {
- background: #C78D46;
- color:#C78D46;
-}
-
-.RED_A {
- background: #F7A1A3;
- color:#F7A1A3;
-}
-
-.RED_B {
- background: #FF8080;
- color:#FF8080;
-}
-
-.RED_C {
- background: #FC6255;
- color:#FC6255;
-}
-
-.RED_D {
- background: #E65A4C;
- color:#E65A4C;
-}
-
-.RED_E {
- background: #CF5044;
- color:#CF5044;
-}
-
-.MAROON_A {
- background: #ECABC1;
- color: #ECABC1;
-}
-
-.MAROON_B {
- background: #EC92AB;
- color: #EC92AB;
-}
-
-.MAROON_C {
- background: #C55F73;
- color: #C55F73;
-}
-
-.MAROON_D {
- background: #A24D61;
- color: #A24D61;
-}
-
-.MAROON_E {
- background: #94424F;
- color: #94424F;
-}
-
-.PURPLE_A {
- background: #CAA3E8;
- color: #CAA3E8;
-}
-
-.PURPLE_B {
- background: #B189C6;
- color: #B189C6;
-}
-
-.PURPLE_C {
- background: #9A72AC;
- color: #9A72AC;
-}
-
-.PURPLE_D {
- background: #715582;
- color: #715582;
-}
-
-.PURPLE_E {
- background: #644172;
- color: #644172;
-}
-
-.GREY_A {
- background: #DDDDDD;
- color: #DDDDDD;
-}
-
-.GREY_B {
- background: #BBBBBB;
- color: #BBBBBB;
-}
-
-.GREY_C {
- background: #888888;
- color: #888888;
-}
-
-.GREY_D {
- background: #444444;
- color: #444444;
-}
-
-.GREY_E {
- background: #222222;
- color: #222222;
-}
-
-.WHITE {
- background: #FFFFFF;
- color: #FFFFFF;
-}
-
-.BLACK {
- background: #000000;
- color: #000000;
-}
-
-.GREY_BROWN {
- background: #736357;
- color: #736357;
-}
-
-.DARK_BROWN {
- background: #8B4513;
- color: #8B4513;
-}
-
-.LIGHT_BROWN {
- background: #CD853F;
- color: #CD853F;
-}
-
-.PINK {
- background: #D147BD;
- color: #D147BD;
-}
-
-.LIGHT_PINK {
- background: #DC75CD;
- color: #DC75CD;
-}
-
-.GREEN_SCREEN {
- background: #00FF00;
- color: #00FF00;
-}
-
-.ORANGE {
- background: #FF862F;
- color: #FF862F;
-}
\ No newline at end of file
diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css
deleted file mode 100644
index 4f2ea8ac..00000000
--- a/docs/source/_static/custom.css
+++ /dev/null
@@ -1,62 +0,0 @@
-p {
- font-size: initial;
-}
-
-span.caption-text {
- font-size: larger;
-}
-
-span.pre {
- font-size: initial;
-}
-
-.highlight-python.notranslate {
- margin-top: 0em;
-}
-
-.manim-video {
- width: 99.9%;
- padding: 8px 0;
- outline: 0;
-}
-
-.manim-example {
- background-color: #333333;
- margin-bottom: 10px;
- box-shadow: 2px 2px 4px #ddd;
-}
-
-.manim-example .manim-video {
- padding: 0;
-}
-
-.manim-example img {
- margin-bottom: 0;
-}
-
-h5.example-header {
- font-size: 18px;
- font-weight: bold;
- padding: 8px 16px;
- color: white;
- margin: 0;
- font-family: inherit;
- text-transform: none;
- margin-top: -0.4em;
- margin-bottom: -0.2em;
-}
-
-.manim-example .highlight {
- background-color: #fafafa;
- border: 2px solid #333333;
- padding: 8px 8px 10px 8px;
- font-size: large;
- margin: 0;
-}
-
-.manim-example .highlight pre {
- background-color: inherit;
- border-left: none;
- margin: 0;
- padding: 0 6px 0 6px;
-}
\ No newline at end of file
diff --git a/docs/source/_static/example_scenes/AnimatingMethods.mp4 b/docs/source/_static/example_scenes/AnimatingMethods.mp4
deleted file mode 100644
index 29cbac62..00000000
Binary files a/docs/source/_static/example_scenes/AnimatingMethods.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/CoordinateSystemExample.mp4 b/docs/source/_static/example_scenes/CoordinateSystemExample.mp4
deleted file mode 100644
index f8f9f582..00000000
Binary files a/docs/source/_static/example_scenes/CoordinateSystemExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/GraphExample.mp4 b/docs/source/_static/example_scenes/GraphExample.mp4
deleted file mode 100644
index a2330c6a..00000000
Binary files a/docs/source/_static/example_scenes/GraphExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/InteractiveDevelopment.mp4 b/docs/source/_static/example_scenes/InteractiveDevelopment.mp4
deleted file mode 100644
index 6c235ec9..00000000
Binary files a/docs/source/_static/example_scenes/InteractiveDevelopment.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/OpeningManimExample.mp4 b/docs/source/_static/example_scenes/OpeningManimExample.mp4
deleted file mode 100644
index 7c070683..00000000
Binary files a/docs/source/_static/example_scenes/OpeningManimExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/SquareToCircle.mp4 b/docs/source/_static/example_scenes/SquareToCircle.mp4
deleted file mode 100644
index 009d6131..00000000
Binary files a/docs/source/_static/example_scenes/SquareToCircle.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/SurfaceExample.mp4 b/docs/source/_static/example_scenes/SurfaceExample.mp4
deleted file mode 100644
index 076a1875..00000000
Binary files a/docs/source/_static/example_scenes/SurfaceExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/TexTransformExample.mp4 b/docs/source/_static/example_scenes/TexTransformExample.mp4
deleted file mode 100644
index 72c556e7..00000000
Binary files a/docs/source/_static/example_scenes/TexTransformExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/TextExample.mp4 b/docs/source/_static/example_scenes/TextExample.mp4
deleted file mode 100644
index 6d18ae93..00000000
Binary files a/docs/source/_static/example_scenes/TextExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/example_scenes/UpdatersExample.mp4 b/docs/source/_static/example_scenes/UpdatersExample.mp4
deleted file mode 100644
index f95afa88..00000000
Binary files a/docs/source/_static/example_scenes/UpdatersExample.mp4 and /dev/null differ
diff --git a/docs/source/_static/manim_shaders_process_en.png b/docs/source/_static/manim_shaders_process_en.png
deleted file mode 100644
index e6bb3b0a..00000000
Binary files a/docs/source/_static/manim_shaders_process_en.png and /dev/null differ
diff --git a/docs/source/_static/manim_shaders_structure.png b/docs/source/_static/manim_shaders_structure.png
deleted file mode 100644
index 66a545c2..00000000
Binary files a/docs/source/_static/manim_shaders_structure.png and /dev/null differ
diff --git a/docs/source/_static/quickstart/SquareToCircle.mp4 b/docs/source/_static/quickstart/SquareToCircle.mp4
deleted file mode 100644
index 009d6131..00000000
Binary files a/docs/source/_static/quickstart/SquareToCircle.mp4 and /dev/null differ
diff --git a/docs/source/_static/quickstart/SquareToCircle.png b/docs/source/_static/quickstart/SquareToCircle.png
deleted file mode 100644
index bbccc3d7..00000000
Binary files a/docs/source/_static/quickstart/SquareToCircle.png and /dev/null differ
diff --git a/docs/source/_static/quickstart/SquareToCircleEmbed.mp4 b/docs/source/_static/quickstart/SquareToCircleEmbed.mp4
deleted file mode 100644
index dd52bc46..00000000
Binary files a/docs/source/_static/quickstart/SquareToCircleEmbed.mp4 and /dev/null differ
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 10ef5843..e9d0844c 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -31,7 +31,10 @@ master_doc = 'index'
pygments_style = 'default'
html_static_path = ["_static"]
-html_css_files = ["custom.css", "colors.css"]
+html_css_files = [
+ "https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/custom.css",
+ "https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/colors.css"
+]
html_theme = 'furo' # pip install furo==2020.10.5b9
html_favicon = '_static/icon.png'
html_logo = '../../logo/transparent_graph.png'
diff --git a/docs/source/getting_started/example_scenes.rst b/docs/source/getting_started/example_scenes.rst
index 203c84e7..a36caf83 100644
--- a/docs/source/getting_started/example_scenes.rst
+++ b/docs/source/getting_started/example_scenes.rst
@@ -9,7 +9,7 @@ InteractiveDevlopment
---------------------
.. manim-example:: InteractiveDevelopment
- :media: ../_static/example_scenes/InteractiveDevelopment.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/InteractiveDevelopment.mp4
from manimlib import *
@@ -66,7 +66,7 @@ AnimatingMethods
----------------
.. manim-example:: AnimatingMethods
- :media: ../_static/example_scenes/AnimatingMethods.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/AnimatingMethods.mp4
class AnimatingMethods(Scene):
def construct(self):
@@ -124,7 +124,7 @@ TextExample
-----------
.. manim-example:: TextExample
- :media: ../_static/example_scenes/TextExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/TextExample.mp4
class TextExample(Scene):
def construct(self):
@@ -178,7 +178,7 @@ TexTransformExample
-------------------
.. manim-example:: TexTransformExample
- :media: ../_static/example_scenes/TexTransformExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/TexTransformExample.mp4
class TexTransformExample(Scene):
def construct(self):
@@ -303,7 +303,7 @@ UpdatersExample
---------------
.. manim-example:: UpdatersExample
- :media: ../_static/example_scenes/UpdatersExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/UpdatersExample.mp4
class UpdatersExample(Scene):
def construct(self):
@@ -388,7 +388,7 @@ CoordinateSystemExample
-----------------------
.. manim-example:: CoordinateSystemExample
- :media: ../_static/example_scenes/CoordinateSystemExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/CoordinateSystemExample.mp4
class CoordinateSystemExample(Scene):
def construct(self):
@@ -472,7 +472,7 @@ GraphExample
------------
.. manim-example:: GraphExample
- :media: ../_static/example_scenes/GraphExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/GraphExample.mp4
class GraphExample(Scene):
def construct(self):
@@ -558,7 +558,7 @@ SurfaceExample
--------------
.. manim-example:: SurfaceExample
- :media: ../_static/example_scenes/SurfaceExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/SurfaceExample.mp4
class SurfaceExample(Scene):
CONFIG = {
@@ -659,7 +659,7 @@ OpeningManimExample
-------------------
.. manim-example:: OpeningManimExample
- :media: ../_static/example_scenes/OpeningManimExample.mp4
+ :media: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/example_scenes/OpeningManimExample.mp4
class OpeningManimExample(Scene):
diff --git a/docs/source/getting_started/quickstart.rst b/docs/source/getting_started/quickstart.rst
index 0151b6fd..4e79e693 100644
--- a/docs/source/getting_started/quickstart.rst
+++ b/docs/source/getting_started/quickstart.rst
@@ -59,7 +59,7 @@ At this time, no window will pop up. When the program is finished, this rendered
image will be automatically opened (saved in the subdirectory ``images/`` of the same
level directory of ``start.py`` by default):
-.. image:: ../_static/quickstart/SquareToCircle.png
+.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/quickstart/SquareToCircle.png
:align: center
Make an image
@@ -162,7 +162,7 @@ opened after the operation is over:
.. raw:: html
-
+
Let's take a look at the code this time. The first 7 lines are the same as the previous
ones, and the 8th line is similar to the 5th line, which creates an instance of the
@@ -237,7 +237,7 @@ You will get an animation similar to the following:
.. raw:: html
-
+
If you want to enter the interactive mode directly, you don't have to write an
empty scene containing only ``self.embed()``, you can directly run the following command
diff --git a/docs/source/getting_started/structure.rst b/docs/source/getting_started/structure.rst
index 6c5f3375..9214989f 100644
--- a/docs/source/getting_started/structure.rst
+++ b/docs/source/getting_started/structure.rst
@@ -120,9 +120,9 @@ Inheritance structure of manim's classes
is a pdf showed inheritance structure of manim's classes, large,
but basically all classes have included:
-.. image:: ../_static/manim_shaders_structure.png
+.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/manim_shaders_structure.png
Manim execution process
-----------------------
-.. image:: ../_static/manim_shaders_process_en.png
+.. image:: https://cdn.jsdelivr.net/gh/manim-kindergarten/CDN@master/manimgl_assets/manim_shaders_process_en.png
diff --git a/docs/source/index.rst b/docs/source/index.rst
index e12b99c7..4892ec10 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,7 +1,7 @@
Manim's documentation
=====================
-.. image:: ../../logo/white_with_name.png
+.. image:: https://cdn.jsdelivr.net/gh/3b1b/manim@master/logo/white_with_name.png
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as seen in the videos
at `3Blue1Brown `_.
diff --git a/example_scenes.py b/example_scenes.py
index 799c50c0..a3a4171f 100644
--- a/example_scenes.py
+++ b/example_scenes.py
@@ -639,14 +639,14 @@ class ControlsExample(Scene):
self.checkbox = Checkbox()
self.color_picker = ColorSliders()
self.panel = ControlPanel(
- Text("Text", size=0.5), self.textbox, Line(),
- Text("Show/Hide Text", size=0.5), self.checkbox, Line(),
- Text("Color of Text", size=0.5), self.color_picker
+ Text("Text", font_size=24), self.textbox, Line(),
+ Text("Show/Hide Text", font_size=24), self.checkbox, Line(),
+ Text("Color of Text", font_size=24), self.color_picker
)
self.add(self.panel)
def construct(self):
- text = Text("", size=2)
+ text = Text("text", font_size=96)
def text_updater(old_text):
assert(isinstance(old_text, Text))
diff --git a/manimlib/mobject/interactive.py b/manimlib/mobject/interactive.py
index df9e0432..617449be 100644
--- a/manimlib/mobject/interactive.py
+++ b/manimlib/mobject/interactive.py
@@ -439,7 +439,7 @@ class ControlPanel(Group):
},
"opener_text_kwargs": {
"text": "Control Panel",
- "size": 0.4
+ "font_size": 20
}
}
diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py
index abd90a83..870cbb32 100644
--- a/manimlib/mobject/mobject.py
+++ b/manimlib/mobject/mobject.py
@@ -57,9 +57,7 @@ class Mobject(object):
# Must match in attributes of vert shader
"shader_dtype": [
('point', np.float32, (3,)),
- ],
- # Event listener
- "listen_to_events": False
+ ]
}
def __init__(self, **kwargs):
diff --git a/manimlib/mobject/svg/text_mobject.py b/manimlib/mobject/svg/text_mobject.py
index 1c11c78c..1719bdae 100644
--- a/manimlib/mobject/svg/text_mobject.py
+++ b/manimlib/mobject/svg/text_mobject.py
@@ -2,8 +2,12 @@ import copy
import hashlib
import os
import re
+import io
import typing
import warnings
+import xml.etree.ElementTree as ET
+import functools
+
from contextlib import contextmanager
from pathlib import Path
@@ -15,8 +19,7 @@ from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.utils.config_ops import digest_config
from manimlib.utils.customization import get_customization
from manimlib.utils.directories import get_downloads_dir, get_text_dir
-from manimpango import PangoUtils
-from manimpango import TextSetting
+from manimpango import PangoUtils, TextSetting, MarkupUtils
TEXT_MOB_SCALE_FACTOR = 0.0076
DEFAULT_LINE_SPACING_SCALE = 0.3
@@ -80,11 +83,6 @@ class Text(SVGMobject):
if self.height is None:
self.scale(TEXT_MOB_SCALE_FACTOR)
- # Just a temporary hack to get better triangulation
- # See pr #1552 for details
- for i in self.submobjects:
- i.insert_n_curves(len(i.get_points()))
-
def remove_empty_path(self, file_name):
with open(file_name, 'r') as fpr:
content = fpr.read()
@@ -243,6 +241,237 @@ class Text(SVGMobject):
)
+class MarkupText(SVGMobject):
+ CONFIG = {
+ # Mobject
+ "color": WHITE,
+ "height": None,
+ # Text
+ "font": '',
+ "font_size": 48,
+ "lsh": None,
+ "justify": False,
+ "slant": NORMAL,
+ "weight": NORMAL,
+ "tab_width": 4,
+ "gradient": None,
+ "disable_ligatures": True,
+ }
+ def __init__(self, text, **config):
+ digest_config(self, config)
+ self.text = f'{text}'
+ self.original_text = self.text
+ self.text_for_parsing = self.text
+ text_without_tabs = text
+ if "\t" in text:
+ text_without_tabs = text.replace("\t", " " * self.tab_width)
+ try:
+ colormap = self.extract_color_tags()
+ gradientmap = self.extract_gradient_tags()
+ except ET.ParseError:
+ # let pango handle that error
+ pass
+ validate_error = MarkupUtils.validate(self.text)
+ if validate_error:
+ raise ValueError(validate_error)
+ file_name = self.text2svg()
+ PangoUtils.remove_last_M(file_name)
+ super().__init__(
+ file_name,
+ **config,
+ )
+ self.chars = self.get_group_class()(*self.submobjects)
+ self.text = text_without_tabs.replace(" ", "").replace("\n", "")
+ if self.gradient:
+ self.set_color_by_gradient(*self.gradient)
+ for col in colormap:
+ self.chars[
+ col["start"]
+ - col["start_offset"] : col["end"]
+ - col["start_offset"]
+ - col["end_offset"]
+ ].set_color(self._parse_color(col["color"]))
+ for grad in gradientmap:
+ self.chars[
+ grad["start"]
+ - grad["start_offset"] : grad["end"]
+ - grad["start_offset"]
+ - grad["end_offset"]
+ ].set_color_by_gradient(
+ *(self._parse_color(grad["from"]), self._parse_color(grad["to"]))
+ )
+ # anti-aliasing
+ if self.height is None:
+ self.scale(TEXT_MOB_SCALE_FACTOR)
+ def text2hash(self):
+ """Generates ``sha256`` hash for file name."""
+ settings = (
+ "MARKUPPANGO" + self.font + self.slant + self.weight + self.color
+ ) # to differentiate from classical Pango Text
+ settings += str(self.lsh) + str(self.font_size)
+ settings += str(self.disable_ligatures)
+ settings += str(self.justify)
+ id_str = self.text + settings
+ hasher = hashlib.sha256()
+ hasher.update(id_str.encode())
+ return hasher.hexdigest()[:16]
+
+ def text2svg(self):
+ """Convert the text to SVG using Pango."""
+ size = self.font_size
+ dir_name = get_text_dir()
+ disable_liga = self.disable_ligatures
+ if not os.path.exists(dir_name):
+ os.makedirs(dir_name)
+ hash_name = self.text2hash()
+ file_name = os.path.join(dir_name, hash_name) + ".svg"
+ if os.path.exists(file_name):
+ return file_name
+
+ extra_kwargs = {}
+ extra_kwargs['justify'] = self.justify
+ extra_kwargs['pango_width'] = DEFAULT_PIXEL_WIDTH - 100
+ if self.lsh:
+ extra_kwargs['line_spacing']=self.lsh
+ return MarkupUtils.text2svg(
+ f'{self.text}',
+ self.font,
+ self.slant,
+ self.weight,
+ size,
+ 0, # empty parameter
+ disable_liga,
+ file_name,
+ START_X,
+ START_Y,
+ DEFAULT_PIXEL_WIDTH, # width
+ DEFAULT_PIXEL_HEIGHT, # height
+ **extra_kwargs
+ )
+
+
+ def _parse_color(self, col):
+ """Parse color given in ```` or ```` tags."""
+ if re.match("#[0-9a-f]{6}", col):
+ return col
+ else:
+ return globals()[col.upper()] # this is hacky
+
+ @functools.lru_cache(10)
+ def get_text_from_markup(self, element=None):
+ if not element:
+ element = ET.fromstring(self.text_for_parsing)
+ final_text = ''
+ for i in element.itertext():
+ final_text += i
+ return final_text
+
+ def extract_color_tags(self, text=None, colormap = None):
+ """Used to determine which parts (if any) of the string should be formatted
+ with a custom color.
+ Removes the ```` tag, as it is not part of Pango's markup and would cause an error.
+ Note: Using the ```` tags is deprecated. As soon as the legacy syntax is gone, this function
+ will be removed.
+ """
+ if not text:
+ text = self.text_for_parsing
+ if not colormap:
+ colormap = list()
+ elements = ET.fromstring(text)
+ text_from_markup = self.get_text_from_markup()
+ final_xml = ET.fromstring(f'{elements.text if elements.text else ""}')
+ def get_color_map(elements):
+ for element in elements:
+ if element.tag == 'color':
+ element_text = self.get_text_from_markup(element)
+ start = text_from_markup.find(element_text)
+ end = start + len(element_text)
+ offsets = element.get('offset').split(",") if element.get('offset') else [0]
+ start_offset = int(offsets[0]) if offsets[0] else 0
+ end_offset = int(offsets[1]) if len(offsets) == 2 and offsets[1] else 0
+ colormap.append(
+ {
+ "start": start,
+ "end": end,
+ "color": element.get('col'),
+ "start_offset": start_offset,
+ "end_offset": end_offset,
+ }
+ )
+
+ _elements_list = list(element.iter())
+ if len(_elements_list) <= 1:
+ final_xml.append(ET.fromstring(f'{element.text if element.text else ""}'))
+ else:
+ final_xml.append(_elements_list[-1])
+ else:
+ if len(list(element.iter())) == 1:
+ final_xml.append(element)
+ else:
+ get_color_map(element)
+ get_color_map(elements)
+ with io.BytesIO() as f:
+ tree = ET.ElementTree()
+ tree._setroot(final_xml)
+ tree.write(f)
+ self.text = f.getvalue().decode()
+ self.text_for_parsing = self.text # gradients will use it
+ return colormap
+
+ def extract_gradient_tags(self, text=None,gradientmap=None):
+ """Used to determine which parts (if any) of the string should be formatted
+ with a gradient.
+ Removes the ```` tag, as it is not part of Pango's markup and would cause an error.
+ """
+ if not text:
+ text = self.text_for_parsing
+ if not gradientmap:
+ gradientmap = list()
+
+ elements = ET.fromstring(text)
+ text_from_markup = self.get_text_from_markup()
+ final_xml = ET.fromstring(f'{elements.text if elements.text else ""}')
+ def get_gradient_map(elements):
+ for element in elements:
+ if element.tag == 'gradient':
+ element_text = self.get_text_from_markup(element)
+ start = text_from_markup.find(element_text)
+ end = start + len(element_text)
+ offsets = element.get('offset').split(",") if element.get('offset') else [0]
+ start_offset = int(offsets[0]) if offsets[0] else 0
+ end_offset = int(offsets[1]) if len(offsets) == 2 and offsets[1] else 0
+ gradientmap.append(
+ {
+ "start": start,
+ "end": end,
+ "from": element.get('from'),
+ "to": element.get('to'),
+ "start_offset": start_offset,
+ "end_offset": end_offset,
+ }
+ )
+ _elements_list = list(element.iter())
+ if len(_elements_list) == 1:
+ final_xml.append(ET.fromstring(f'{element.text if element.text else ""}'))
+ else:
+ final_xml.append(_elements_list[-1])
+ else:
+ if len(list(element.iter())) == 1:
+ final_xml.append(element)
+ else:
+ get_gradient_map(element)
+ get_gradient_map(elements)
+ with io.BytesIO() as f:
+ tree = ET.ElementTree()
+ tree._setroot(final_xml)
+ tree.write(f)
+ self.text = f.getvalue().decode()
+
+ return gradientmap
+
+ def __repr__(self):
+ return f"MarkupText({repr(self.original_text)})"
+
@contextmanager
def register_font(font_file: typing.Union[str, Path]):
"""Temporarily add a font file to Pango's search path.
diff --git a/manimlib/once_useful_constructs/counting.py b/manimlib/once_useful_constructs/counting.py
index 181b5b32..ad54da2e 100644
--- a/manimlib/once_useful_constructs/counting.py
+++ b/manimlib/once_useful_constructs/counting.py
@@ -10,6 +10,7 @@ from manimlib.mobject.svg.tex_mobject import Tex
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.scene.scene import Scene
+import itertools as it
class CountingScene(Scene):
CONFIG = {
@@ -219,7 +220,7 @@ class CountInTernary(PowerCounter):
def construct(self):
self.count(27)
- # def get_template_configuration(self):
+ # def get_template_configuration(self, place):
# return [ORIGIN, UP]
@@ -233,7 +234,7 @@ class CountInBinaryTo256(PowerCounter):
def construct(self):
self.count(128, 0.3)
- def get_template_configuration(self):
+ def get_template_configuration(self, place):
return [ORIGIN, UP]
diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py
index ad2f3838..a65b662e 100644
--- a/manimlib/scene/scene.py
+++ b/manimlib/scene/scene.py
@@ -50,7 +50,7 @@ class Scene(object):
self.camera = self.camera_class(**self.camera_config)
self.file_writer = SceneFileWriter(self, **self.file_writer_config)
- self.mobjects = []
+ self.mobjects = [self.camera.frame]
self.num_plays = 0
self.time = 0
self.skip_time = 0
@@ -570,7 +570,7 @@ class Scene(object):
frame = self.camera.frame
if self.window.is_key_pressed(ord("z")):
factor = 1 + np.arctan(10 * offset[1])
- frame.scale(factor, about_point=point)
+ frame.scale(1/factor, about_point=point)
else:
transform = frame.get_inverse_camera_rotation_matrix()
shift = np.dot(np.transpose(transform), offset)
diff --git a/manimlib/utils/init_config.py b/manimlib/utils/init_config.py
index f126849f..10a0b8ba 100644
--- a/manimlib/utils/init_config.py
+++ b/manimlib/utils/init_config.py
@@ -23,7 +23,7 @@ def init_customization():
"background_color": "",
},
"window_position": "UR",
- "window_position": 0,
+ "window_monitor": 0,
"break_into_partial_movies": False,
"camera_qualities": {
"low": {
diff --git a/manimlib/utils/space_ops.py b/manimlib/utils/space_ops.py
index efa98f7a..b2aa46c1 100644
--- a/manimlib/utils/space_ops.py
+++ b/manimlib/utils/space_ops.py
@@ -359,81 +359,64 @@ def earclip_triangulation(verts, ring_ends):
the ends of new paths are
"""
- # First, connect all the rings so that the polygon
- # with holes is instead treated as a (very convex)
- # polygon with one edge. Do this by drawing connections
- # between rings close to each other
rings = [
list(range(e0, e1))
for e0, e1 in zip([0, *ring_ends], ring_ends)
]
+
+ def is_in(point, ring_id):
+ return abs(abs(get_winding_number([i - point for i in verts[rings[ring_id]]])) - 1) < 1e-5
+
+ def ring_area(ring_id):
+ ring = rings[ring_id]
+ s = 0
+ for i, j in zip(ring[1:], ring):
+ s += cross2d(verts[i], verts[j])
+ return abs(s) / 2
+
# Points at the same position may cause problems
for i in rings:
- verts[i[0]] += (verts[i[1]] - verts[i[0]]) * 5e-6
- verts[i[-1]] += (verts[i[-2]] - verts[i[-1]]) * 5e-6
- attached_rings = rings[:1]
- detached_rings = rings[1:]
- loop_connections = dict()
+ verts[i[0]] += (verts[i[1]]-verts[i[0]]) * 1e-6
+ verts[i[-1]] += (verts[i[-2]]-verts[i[-1]]) * 1e-6
- while detached_rings:
- i_range, j_range = [
- list(filter(
- # Ignore indices that are already being
- # used to draw some connection
- lambda i: i not in loop_connections,
- it.chain(*ring_group)
- ))
- for ring_group in (attached_rings, detached_rings)
- ]
+ # First, we should know which rings are directly contained in it for each ring
- # Closet point on the atttached rings to an estimated midpoint
- # of the detached rings
- tmp_j_vert = midpoint(
- verts[j_range[0]],
- verts[j_range[len(j_range) // 2]]
- )
- i = min(i_range, key=lambda i: norm_squared(verts[i] - tmp_j_vert))
- # Closet point of the detached rings to the aforementioned
- # point of the attached rings
- j = min(j_range, key=lambda j: norm_squared(verts[i] - verts[j]))
- # Recalculate i based on new j
- i = min(i_range, key=lambda i: norm_squared(verts[i] - verts[j]))
+ right = [max(verts[rings[i], 0]) for i in range(len(rings))]
+ left = [min(verts[rings[i], 0]) for i in range(len(rings))]
+ top = [max(verts[rings[i], 1]) for i in range(len(rings))]
+ bottom = [min(verts[rings[i], 1]) for i in range(len(rings))]
+ area = [ring_area(i) for i in range(len(rings))]
- # Remember to connect the polygon at these points
- loop_connections[i] = j
- loop_connections[j] = i
+ # The larger ring must be outside
+ rings_sorted = list(range(len(rings)))
+ rings_sorted.sort(key=lambda x: area[x], reverse=True)
- # Move the ring which j belongs to from the
- # attached list to the detached list
- new_ring = next(filter(
- lambda ring: ring[0] <= j <= ring[-1],
- detached_rings
- ))
- detached_rings.remove(new_ring)
- attached_rings.append(new_ring)
+ def is_in_fast(ring_a, ring_b):
+ # Whether a is in b
+ return (left[ring_b] <= left[ring_a] <= right[ring_a] <= right[ring_b] and
+ bottom[ring_b] <= bottom[ring_a] <= top[ring_a] <= top[ring_b] and
+ is_in(verts[rings[ring_a][0]], ring_b))
- # Setup linked list
- after = []
- end0 = 0
- for end1 in ring_ends:
- after.extend(range(end0 + 1, end1))
- after.append(end0)
- end0 = end1
+ chilren = [[] for i in rings]
+ for idx, i in enumerate(rings_sorted):
+ for j in rings_sorted[:idx][::-1]:
+ if is_in_fast(i, j):
+ chilren[j].append(i)
+ break
- # Find an ordering of indices walking around the polygon
- indices = []
- i = 0
- for x in range(len(verts) + len(ring_ends) - 1):
- # starting = False
- if i in loop_connections:
- j = loop_connections[i]
- indices.extend([i, j])
- i = after[j]
- else:
- indices.append(i)
- i = after[i]
- if i == 0:
- break
+ res = []
- meta_indices = earcut(verts[indices, :2], [len(indices)])
- return [indices[mi] for mi in meta_indices]
+ # Then, we can use earcut for each part
+ used = [False] * len(rings)
+ for i in rings_sorted:
+ if used[i]:
+ continue
+ v = rings[i]
+ ring_ends = [len(v)]
+ for j in chilren[i]:
+ used[j] = True
+ v += rings[j]
+ ring_ends.append(len(v))
+ res += [v[i] for i in earcut(verts[v, :2], ring_ends)]
+
+ return res
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..07de284a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools", "wheel"]
+build-backend = "setuptools.build_meta"
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 01df3b6f..c35e1c3a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,22 +1,43 @@
[metadata]
name = manimgl
+version = 1.0.1
author = Grant Sanderson
-author-email= grant@3blue1brown.com
-summary = Animation engine for explanatory math videos
-description-file = README.md
-description-content-type = text/markdown; charset=UTF-8
-home-page = https://github.com/3b1b/manim
+author_email= grant@3blue1brown.com
+description = Animation engine for explanatory math videos
+long_description = file: README.md
+long_description_content_type = text/markdown; charset=UTF-8
+home_page = https://github.com/3b1b/manim
project_urls =
Bug Tracker = https://github.com/3b1b/manim/issues
Documentation = https://3b1b.github.io/manim/
Source Code = https://github.com/3b1b/manim
license = MIT
-[files]
-packages = manimlib
-extra_files = requirements.txt
+[options]
+packages = find:
+include_package_data=True
+install_requires =
+ argparse
+ colour
+ numpy
+ Pillow
+ scipy
+ sympy
+ tqdm
+ mapbox-earcut
+ matplotlib
+ moderngl
+ moderngl_window
+ pydub
+ pyyaml
+ screeninfo
+ pyreadline; sys_platform == 'win32'
+ validators
+ ipython
+ PyOpenGL
+ manimpango>=0.2.0,<0.4.0
-[entry_points]
+[options.entry_points]
console_scripts =
manimgl = manimlib.__main__:main
manim-render = manimlib.__main__:main
diff --git a/setup.py b/setup.py
deleted file mode 100644
index aa2d8a01..00000000
--- a/setup.py
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env python
-
-from setuptools import setup
-
-setup(
- setup_requires=['pbr'],
- pbr=True,
-)