From 7d534ab0d4c3982eaf30dd51c0e7e1ad19b1863e Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Tue, 30 Oct 2018 06:39:45 +0300 Subject: [PATCH 01/11] Add a testing script --- constants.py | 2 +- test1.py | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test1.py diff --git a/constants.py b/constants.py index af3fc0ef..4478e211 100644 --- a/constants.py +++ b/constants.py @@ -20,7 +20,7 @@ elif os.path.exists("media_dir.txt"): else: MEDIA_DIR = os.path.join( os.path.expanduser('~'), - "Dropbox (3Blue1Brown)/3Blue1Brown Team Folder" + "Videos/" ) if not os.path.exists(MEDIA_DIR): diff --git a/test1.py b/test1.py new file mode 100644 index 00000000..5fca056b --- /dev/null +++ b/test1.py @@ -0,0 +1,17 @@ +from big_ol_pile_of_manim_imports import * + +class Shapes(Scene): + #A few simple shapes + def construct(self): + circle = Circle() + square = Square() + line=Line(np.array([3,0,0]),np.array([5,0,0])) + triangle=Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) + + self.add(line) + self.play(ShowCreation(circle)) + self.play(FadeOut(circle)) + self.play(GrowFromCenter(square)) + self.play(Transform(square,triangle)) + +#python3 extract_scene.py test1.py Shapes -r 1080 From 5e8a2c8901b3a72bec561dbab063c8197d18cd3a Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Tue, 30 Oct 2018 07:37:32 +0300 Subject: [PATCH 02/11] Add the top-level manim module and Manim class --- manim.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ test1.py | 6 ++++++ 2 files changed, 52 insertions(+) create mode 100644 manim.py diff --git a/manim.py b/manim.py new file mode 100644 index 00000000..26459d05 --- /dev/null +++ b/manim.py @@ -0,0 +1,46 @@ +#!/usr/bin/python3 + +import sys +import argparse +# import imp +import importlib +import inspect +import itertools as it +import os +import subprocess as sp +import traceback + +from constants import * + +from scene.scene import Scene +from utils.sounds import play_error_sound +from utils.sounds import play_finish_sound + +from colour import Color + + +class Manim(): + + def __init__(self): + self.config = { + "file": "example_file.py", + "scene_name": "LiveStream", + "open_video_upon_completion": False, + "show_file_in_finder": False, + # By default, write to file + "write_to_movie": True, + "show_last_frame": False, + "save_pngs": False, + # If -t is passed in (for transparent), this will be RGBA + "saved_image_mode": "RGB", + "movie_file_extension": ".mp4", + "quiet": True, + "ignore_waits": False, + "write_all": False, + "name": "LiveStream", + "start_at_animation_number": 0, + "end_at_animation_number": None, + "skip_animations": False, + "camera_config": HIGH_QUALITY_CAMERA_CONFIG, + "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, + } diff --git a/test1.py b/test1.py index 5fca056b..23613c18 100644 --- a/test1.py +++ b/test1.py @@ -1,4 +1,5 @@ from big_ol_pile_of_manim_imports import * +from manim import Manim class Shapes(Scene): #A few simple shapes @@ -15,3 +16,8 @@ class Shapes(Scene): self.play(Transform(square,triangle)) #python3 extract_scene.py test1.py Shapes -r 1080 + + +if __name__ == '__main__': + manim = Manim() + Shapes(**manim.config) From 80256013ea4f2daae665c7ed1353ab0575459422 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Tue, 30 Oct 2018 09:00:51 +0300 Subject: [PATCH 03/11] Add is_live_streaming control flow to idle the scene generation pipe for interactive shell usage purpose --- manim.py | 1 + scene/scene.py | 2 ++ test1.py | 22 +++++++++++++++++----- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/manim.py b/manim.py index 26459d05..c6cf2292 100644 --- a/manim.py +++ b/manim.py @@ -43,4 +43,5 @@ class Manim(): "skip_animations": False, "camera_config": HIGH_QUALITY_CAMERA_CONFIG, "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, + "is_live_streaming": True, } diff --git a/scene/scene.py b/scene/scene.py index a152334d..f116cf52 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -67,6 +67,8 @@ class Scene(Container): self.setup() if self.write_to_movie: self.open_movie_pipe() + if self.is_live_streaming: + return None try: self.construct(*self.construct_args) except EndSceneEarlyException: diff --git a/test1.py b/test1.py index 23613c18..6ec41557 100644 --- a/test1.py +++ b/test1.py @@ -6,8 +6,8 @@ class Shapes(Scene): def construct(self): circle = Circle() square = Square() - line=Line(np.array([3,0,0]),np.array([5,0,0])) - triangle=Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) + line = Line(np.array([3,0,0]),np.array([5,0,0])) + triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) self.add(line) self.play(ShowCreation(circle)) @@ -18,6 +18,18 @@ class Shapes(Scene): #python3 extract_scene.py test1.py Shapes -r 1080 -if __name__ == '__main__': - manim = Manim() - Shapes(**manim.config) +manim = Manim() +scene1 = Scene(**manim.config) + +circle = Circle() +square = Square() +line = Line(np.array([3,0,0]),np.array([5,0,0])) +triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) + +scene1.add(line) +scene1.play(ShowCreation(circle)) +scene1.play(FadeOut(circle)) +scene1.play(GrowFromCenter(square)) +scene1.play(Transform(square,triangle)) + +scene1.close_movie_pipe() From a4a5e79ddf86f3314c0f65ea7b9121f6dd73b869 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Tue, 30 Oct 2018 18:25:01 +0300 Subject: [PATCH 04/11] Return a Scene object from the creation of a Manim instance --- manim.py | 20 +++----------------- test1.py | 13 ++++++------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/manim.py b/manim.py index c6cf2292..bb597ba9 100644 --- a/manim.py +++ b/manim.py @@ -1,28 +1,13 @@ #!/usr/bin/python3 -import sys -import argparse -# import imp -import importlib -import inspect -import itertools as it -import os -import subprocess as sp -import traceback - from constants import * - from scene.scene import Scene -from utils.sounds import play_error_sound -from utils.sounds import play_finish_sound - -from colour import Color class Manim(): - def __init__(self): - self.config = { + def __new__(cls): + kwargs = { "file": "example_file.py", "scene_name": "LiveStream", "open_video_upon_completion": False, @@ -45,3 +30,4 @@ class Manim(): "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, "is_live_streaming": True, } + return Scene(**kwargs) diff --git a/test1.py b/test1.py index 6ec41557..60ebfba5 100644 --- a/test1.py +++ b/test1.py @@ -19,17 +19,16 @@ class Shapes(Scene): manim = Manim() -scene1 = Scene(**manim.config) circle = Circle() square = Square() line = Line(np.array([3,0,0]),np.array([5,0,0])) triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) -scene1.add(line) -scene1.play(ShowCreation(circle)) -scene1.play(FadeOut(circle)) -scene1.play(GrowFromCenter(square)) -scene1.play(Transform(square,triangle)) +manim.add(line) +manim.play(ShowCreation(circle)) +manim.play(FadeOut(circle)) +manim.play(GrowFromCenter(square)) +manim.play(Transform(square,triangle)) -scene1.close_movie_pipe() +manim.close_movie_pipe() From 312c2b02432c24a8a121e2f80226f4557e9b6589 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Tue, 30 Oct 2018 18:30:34 +0300 Subject: [PATCH 05/11] Turn live stream related configurations into constants --- constants.py | 3 +++ manim.py | 7 +++---- test1.py | 16 ---------------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/constants.py b/constants.py index 4478e211..14ef7de8 100644 --- a/constants.py +++ b/constants.py @@ -211,3 +211,6 @@ PALETTE = list(COLOR_MAP.values()) locals().update(COLOR_MAP) for name in [s for s in list(COLOR_MAP.keys()) if s.endswith("_C")]: locals()[name.replace("_C", "")] = locals()[name] + +IS_LIVE_STREAMING = True +LIVE_STREAM_NAME = "LiveStream" diff --git a/manim.py b/manim.py index bb597ba9..0448d1e4 100644 --- a/manim.py +++ b/manim.py @@ -8,8 +8,7 @@ class Manim(): def __new__(cls): kwargs = { - "file": "example_file.py", - "scene_name": "LiveStream", + "scene_name": LIVE_STREAM_NAME, "open_video_upon_completion": False, "show_file_in_finder": False, # By default, write to file @@ -22,12 +21,12 @@ class Manim(): "quiet": True, "ignore_waits": False, "write_all": False, - "name": "LiveStream", + "name": LIVE_STREAM_NAME, "start_at_animation_number": 0, "end_at_animation_number": None, "skip_animations": False, "camera_config": HIGH_QUALITY_CAMERA_CONFIG, "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, - "is_live_streaming": True, + "is_live_streaming": IS_LIVE_STREAMING } return Scene(**kwargs) diff --git a/test1.py b/test1.py index 60ebfba5..ab2e173b 100644 --- a/test1.py +++ b/test1.py @@ -1,22 +1,6 @@ from big_ol_pile_of_manim_imports import * from manim import Manim -class Shapes(Scene): - #A few simple shapes - def construct(self): - circle = Circle() - square = Square() - line = Line(np.array([3,0,0]),np.array([5,0,0])) - triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) - - self.add(line) - self.play(ShowCreation(circle)) - self.play(FadeOut(circle)) - self.play(GrowFromCenter(square)) - self.play(Transform(square,triangle)) - -#python3 extract_scene.py test1.py Shapes -r 1080 - manim = Manim() From f898644a3cfe1b25fed31af090edf64b964efa03 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Wed, 31 Oct 2018 11:16:18 +0300 Subject: [PATCH 06/11] Add point to point streaming from ffmpeg to mplayer via TCP --- scene/scene.py | 9 ++++++++- test1.py | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/scene/scene.py b/scene/scene.py index f116cf52..5b65b9f0 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -624,13 +624,20 @@ class Scene(Container): '-vcodec', 'libx264', '-pix_fmt', 'yuv420p', ] - command += [temp_file_path] + if self.is_live_streaming: + command += ['-f', 'mpegts'] + command += ['tcp://127.0.0.1:2000'] + else: + command += [temp_file_path] + print(' '.join(command)) # self.writing_process = sp.Popen(command, stdin=sp.PIPE, shell=True) self.writing_process = sp.Popen(command, stdin=sp.PIPE) def close_movie_pipe(self): self.writing_process.stdin.close() self.writing_process.wait() + if self.is_live_streaming: + return True if os.name == 'nt': shutil.move(*self.args_to_rename_file) else: diff --git a/test1.py b/test1.py index ab2e173b..169eb341 100644 --- a/test1.py +++ b/test1.py @@ -16,3 +16,7 @@ manim.play(GrowFromCenter(square)) manim.play(Transform(square,triangle)) manim.close_movie_pipe() + +# mplayer -cache 8092 ffmpeg://tcp://127.0.0.1:2000?listen +# for listening the stream +# start mplayer before running this script From e071ac62a162a19af0def7df44ac700cffded669 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Wed, 31 Oct 2018 21:29:01 +0300 Subject: [PATCH 07/11] Fix streaming issues by migrating from mplayer to ffplay and add Twitch support --- constants.py | 7 +++++++ manim.py | 1 - scene/scene.py | 14 +++++++++----- test1.py | 8 +++++++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/constants.py b/constants.py index 14ef7de8..ca783598 100644 --- a/constants.py +++ b/constants.py @@ -212,5 +212,12 @@ locals().update(COLOR_MAP) for name in [s for s in list(COLOR_MAP.keys()) if s.endswith("_C")]: locals()[name.replace("_C", "")] = locals()[name] +# Streaming related configurations IS_LIVE_STREAMING = True LIVE_STREAM_NAME = "LiveStream" +IS_STREAMING_TO_TWITCH = False +TWITCH_STREAM_KEY = "YOUR_STREAM_KEY" +STREAMING_PROTOCOL = "tcp" +STREAMING_IP = "127.0.0.1" +STREAMING_PORT = "2000" +STREAMING_CLIENT = "ffplay" diff --git a/manim.py b/manim.py index 0448d1e4..49eefad6 100644 --- a/manim.py +++ b/manim.py @@ -27,6 +27,5 @@ class Manim(): "skip_animations": False, "camera_config": HIGH_QUALITY_CAMERA_CONFIG, "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, - "is_live_streaming": IS_LIVE_STREAMING } return Scene(**kwargs) diff --git a/scene/scene.py b/scene/scene.py index 5b65b9f0..95a863ea 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -67,7 +67,7 @@ class Scene(Container): self.setup() if self.write_to_movie: self.open_movie_pipe() - if self.is_live_streaming: + if IS_LIVE_STREAMING: return None try: self.construct(*self.construct_args) @@ -624,9 +624,13 @@ class Scene(Container): '-vcodec', 'libx264', '-pix_fmt', 'yuv420p', ] - if self.is_live_streaming: - command += ['-f', 'mpegts'] - command += ['tcp://127.0.0.1:2000'] + if IS_LIVE_STREAMING: + if IS_STREAMING_TO_TWITCH: + command += ['-f', 'flv'] + command += ['rtmp://live.twitch.tv/app/' + TWITCH_STREAM_KEY] + else: + command += ['-f', 'mpegts'] + command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] else: command += [temp_file_path] print(' '.join(command)) @@ -636,7 +640,7 @@ class Scene(Container): def close_movie_pipe(self): self.writing_process.stdin.close() self.writing_process.wait() - if self.is_live_streaming: + if IS_LIVE_STREAMING: return True if os.name == 'nt': shutil.move(*self.args_to_rename_file) diff --git a/test1.py b/test1.py index 169eb341..70d65e88 100644 --- a/test1.py +++ b/test1.py @@ -9,14 +9,20 @@ square = Square() line = Line(np.array([3,0,0]),np.array([5,0,0])) triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) +manim.wait(3) manim.add(line) +manim.wait(3) manim.play(ShowCreation(circle)) +manim.wait(3) manim.play(FadeOut(circle)) +manim.wait(3) manim.play(GrowFromCenter(square)) +manim.wait(3) manim.play(Transform(square,triangle)) +manim.wait(10) manim.close_movie_pipe() -# mplayer -cache 8092 ffmpeg://tcp://127.0.0.1:2000?listen +# ffplay tcp://127.0.0.1:2000?listen # for listening the stream # start mplayer before running this script From a229bdc9c939f7046a9ddd43efc99245c07bf9f4 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Thu, 1 Nov 2018 11:23:34 +0300 Subject: [PATCH 08/11] Add a stream lock and an idle state for stream to provide continuous frame flow --- scene/scene.py | 21 +++++++++++++++++++++ stream_starter.py | 13 +++++++++++++ test1.py | 28 ---------------------------- 3 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 stream_starter.py delete mode 100644 test1.py diff --git a/scene/scene.py b/scene/scene.py index 95a863ea..f427d1b9 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -7,6 +7,11 @@ import random import shutil import subprocess as sp import warnings +from time import sleep +try: + import thread # Low-level threading API (Python 2.7) +except ImportError: + import _thread as thread # Low-level threading API (Python 3.x) from tqdm import tqdm as ProgressDisplay @@ -58,6 +63,7 @@ class Scene(Container): self.frame_num = 0 self.current_scene_time = 0 self.original_skipping_status = self.skip_animations + self.stream_lock = False if self.name is None: self.name = self.__class__.__name__ if self.random_seed is not None: @@ -455,6 +461,8 @@ class Scene(Container): raise EndSceneEarlyException() def play(self, *args, **kwargs): + if IS_LIVE_STREAMING: + self.stream_lock = False if len(args) == 0: warnings.warn("Called Scene.play with no animations") return @@ -491,8 +499,21 @@ class Scene(Container): else: self.continual_update(0) self.num_plays += 1 + + if IS_LIVE_STREAMING: + self.stream_lock = True + thread.start_new_thread(self.idle_stream, ()) + return self + def idle_stream(self): + while(self.stream_lock): + self.update_frame() + n_frames = 1 + frame = self.get_frame() + self.add_frames(*[frame] * n_frames) + sleep(self.frame_duration * 999/1000) + def clean_up_animations(self, *animations): for animation in animations: animation.clean_up(self) diff --git a/stream_starter.py b/stream_starter.py new file mode 100644 index 00000000..17794c4c --- /dev/null +++ b/stream_starter.py @@ -0,0 +1,13 @@ +from big_ol_pile_of_manim_imports import * +import subprocess +from time import sleep +from manim import Manim + +if not IS_STREAMING_TO_TWITCH: + FNULL = open(os.devnull, 'w') + subprocess.Popen([STREAMING_CLIENT, STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT + '?listen'], stdout=FNULL, stderr=FNULL) + sleep(3) + +manim = Manim() + +print("YOUR STREAM IS READY!") diff --git a/test1.py b/test1.py deleted file mode 100644 index 70d65e88..00000000 --- a/test1.py +++ /dev/null @@ -1,28 +0,0 @@ -from big_ol_pile_of_manim_imports import * -from manim import Manim - - -manim = Manim() - -circle = Circle() -square = Square() -line = Line(np.array([3,0,0]),np.array([5,0,0])) -triangle = Polygon(np.array([0,0,0]),np.array([1,1,0]),np.array([1,-1,0])) - -manim.wait(3) -manim.add(line) -manim.wait(3) -manim.play(ShowCreation(circle)) -manim.wait(3) -manim.play(FadeOut(circle)) -manim.wait(3) -manim.play(GrowFromCenter(square)) -manim.wait(3) -manim.play(Transform(square,triangle)) -manim.wait(10) - -manim.close_movie_pipe() - -# ffplay tcp://127.0.0.1:2000?listen -# for listening the stream -# start mplayer before running this script From 815b3103c4fded4ea9ea875c851b648ed3639521 Mon Sep 17 00:00:00 2001 From: Mehmet Mert Yildiran Date: Thu, 1 Nov 2018 11:45:22 +0300 Subject: [PATCH 09/11] Update README.md --- README.md | 15 +++++++++++++++ constants.py | 4 ++-- scene/scene.py | 1 - 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2bed224..c090d34f 100644 --- a/README.md +++ b/README.md @@ -69,3 +69,18 @@ a Dockerfile provided. On a Windows system, make sure to replace `$PWD` with an absolute path to manim. Note that the shipped Docker image contains the bare requirements to run manim. To transform the Docker container into a fully-functioning development environment, you will have to edit your personal Dockerfile a bit. For a guide to create your own personal Docker image, consult [this guide to Dockerfiles](https://www.howtoforge.com/tutorial/how-to-create-docker-images-with-dockerfile/). + +## Live Streaming + +To live stream your animations, simply assign `IS_LIVE_STREAMING = True` in `constants.py` file and from your Python Interactive Shell (`python3`) import the stream starter with `from stream_starter import *` while under the project directory. This will provide a clean interactive shell to enter your commands. `manim` object is a `Manim()` instance so as soon as you play an animation with `manim.play()` your stream will start. A video player will pop-up and you can broadcast that video using [OBS Studio](https://obsproject.com/) which is the most practical way of streaming with this math animation library. An example: + +``` +>>> from stream_starter import * +YOUR STREAM IS READY! +>>> circle = Circle() +>>> manim.play(ShowCreation(circle)) +Animation 0: ShowCreationCircle: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 60/60 [00:01<00:00, 37.30it/s] + +``` + +It is also possible to stream directly to Twitch. To do that simply assign `IS_STREAMING_TO_TWITCH = True` in `constants.py` file and put your Twitch Stream Key to `TWITCH_STREAM_KEY = "YOUR_STREAM_KEY"` and when you follow the above example the stream will directly start on your Twitch channel(with no audio support). diff --git a/constants.py b/constants.py index ca783598..a5ef2d67 100644 --- a/constants.py +++ b/constants.py @@ -20,7 +20,7 @@ elif os.path.exists("media_dir.txt"): else: MEDIA_DIR = os.path.join( os.path.expanduser('~'), - "Videos/" + "Dropbox (3Blue1Brown)/3Blue1Brown Team Folder" ) if not os.path.exists(MEDIA_DIR): @@ -213,7 +213,7 @@ for name in [s for s in list(COLOR_MAP.keys()) if s.endswith("_C")]: locals()[name.replace("_C", "")] = locals()[name] # Streaming related configurations -IS_LIVE_STREAMING = True +IS_LIVE_STREAMING = False LIVE_STREAM_NAME = "LiveStream" IS_STREAMING_TO_TWITCH = False TWITCH_STREAM_KEY = "YOUR_STREAM_KEY" diff --git a/scene/scene.py b/scene/scene.py index f427d1b9..2e24319a 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -654,7 +654,6 @@ class Scene(Container): command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] else: command += [temp_file_path] - print(' '.join(command)) # self.writing_process = sp.Popen(command, stdin=sp.PIPE, shell=True) self.writing_process = sp.Popen(command, stdin=sp.PIPE) From 846a8249472b2ac9c9f6bede7904b574eb406788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Mert=20Y=C4=B1ld=C4=B1ran?= Date: Fri, 2 Nov 2018 20:48:38 +0300 Subject: [PATCH 10/11] Add automatic positioning for live streams --- animation/creation.py | 11 +++++++++++ position.py | 3 +++ scene/scene.py | 14 +++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 position.py diff --git a/animation/creation.py b/animation/creation.py index 2c82c232..f35e8db5 100644 --- a/animation/creation.py +++ b/animation/creation.py @@ -15,6 +15,8 @@ from utils.paths import counterclockwise_path from utils.rate_functions import double_smooth from utils.rate_functions import smooth +import position + # Drawing @@ -98,6 +100,15 @@ class Write(DrawBorderThenFill): mobject = TextMobject(mob_or_text) else: mobject = mob_or_text + + if IS_LIVE_STREAMING: + print(position.current) + mobject.shift(position.current) + position.current = position.current + 2 * DOWN + if position.current[1] < -3: + position.current[1] = 3 + position.current = position.current + 3 * RIGHT + if "run_time" not in kwargs: self.establish_run_time(mobject) if "lag_factor" not in kwargs: diff --git a/position.py b/position.py new file mode 100644 index 00000000..fd5dcb2c --- /dev/null +++ b/position.py @@ -0,0 +1,3 @@ +from constants import * + +current = 5 * LEFT + 3 * UP diff --git a/scene/scene.py b/scene/scene.py index 2e24319a..71eceea0 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -29,6 +29,9 @@ from utils.output_directory_getters import get_image_output_directory from container.container import Container +import position +from mobject.svg.tex_mobject import TextMobject +from animation.creation import Write class Scene(Container): CONFIG = { @@ -343,6 +346,10 @@ class Scene(Container): self.mobjects = [] self.foreground_mobjects = [] self.continual_animation = [] + + if IS_LIVE_STREAMING: + position.current = 5 * LEFT + 3 * UP + print(position.current) return self def get_mobjects(self): @@ -512,7 +519,7 @@ class Scene(Container): n_frames = 1 frame = self.get_frame() self.add_frames(*[frame] * n_frames) - sleep(self.frame_duration * 999/1000) + sleep(self.frame_duration * 99/100) def clean_up_animations(self, *animations): for animation in animations: @@ -631,6 +638,7 @@ class Scene(Container): '-pix_fmt', 'rgba', '-r', str(fps), # frames per second '-i', '-', # The imput comes from a pipe + '-c:v', 'h264_nvenc', '-an', # Tells FFMPEG not to expect any audio '-loglevel', 'error', ] @@ -667,6 +675,10 @@ class Scene(Container): else: os.rename(*self.args_to_rename_file) + def tex(self, latex): + eq = TextMobject(latex) + self.play(Write(eq)) + class EndSceneEarlyException(Exception): pass From db7f8320bd84e9ebbf75adfcd6dfc480881e5849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Mert=20Y=C4=B1ld=C4=B1ran?= Date: Sat, 3 Nov 2018 21:05:20 +0300 Subject: [PATCH 11/11] Fix the idle_stream()'s sleep duration and change the stream FPS to 30 --- animation/creation.py | 10 ---------- manim.py | 2 +- position.py | 3 --- scene/scene.py | 20 ++++++++++++-------- 4 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 position.py diff --git a/animation/creation.py b/animation/creation.py index f35e8db5..23530d71 100644 --- a/animation/creation.py +++ b/animation/creation.py @@ -15,8 +15,6 @@ from utils.paths import counterclockwise_path from utils.rate_functions import double_smooth from utils.rate_functions import smooth -import position - # Drawing @@ -101,14 +99,6 @@ class Write(DrawBorderThenFill): else: mobject = mob_or_text - if IS_LIVE_STREAMING: - print(position.current) - mobject.shift(position.current) - position.current = position.current + 2 * DOWN - if position.current[1] < -3: - position.current[1] = 3 - position.current = position.current + 3 * RIGHT - if "run_time" not in kwargs: self.establish_run_time(mobject) if "lag_factor" not in kwargs: diff --git a/manim.py b/manim.py index 49eefad6..495db647 100644 --- a/manim.py +++ b/manim.py @@ -26,6 +26,6 @@ class Manim(): "end_at_animation_number": None, "skip_animations": False, "camera_config": HIGH_QUALITY_CAMERA_CONFIG, - "frame_duration": PRODUCTION_QUALITY_FRAME_DURATION, + "frame_duration": MEDIUM_QUALITY_FRAME_DURATION, } return Scene(**kwargs) diff --git a/position.py b/position.py deleted file mode 100644 index fd5dcb2c..00000000 --- a/position.py +++ /dev/null @@ -1,3 +0,0 @@ -from constants import * - -current = 5 * LEFT + 3 * UP diff --git a/scene/scene.py b/scene/scene.py index 71eceea0..34971272 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -18,7 +18,7 @@ from tqdm import tqdm as ProgressDisplay from constants import * from animation.animation import Animation -from animation.transform import MoveToTarget +from animation.transform import MoveToTarget, ApplyMethod from camera.camera import Camera from continual_animation.continual_animation import ContinualAnimation from mobject.mobject import Mobject @@ -29,9 +29,9 @@ from utils.output_directory_getters import get_image_output_directory from container.container import Container -import position from mobject.svg.tex_mobject import TextMobject from animation.creation import Write +import datetime class Scene(Container): CONFIG = { @@ -346,10 +346,6 @@ class Scene(Container): self.mobjects = [] self.foreground_mobjects = [] self.continual_animation = [] - - if IS_LIVE_STREAMING: - position.current = 5 * LEFT + 3 * UP - print(position.current) return self def get_mobjects(self): @@ -515,11 +511,15 @@ class Scene(Container): def idle_stream(self): while(self.stream_lock): + a = datetime.datetime.now() self.update_frame() n_frames = 1 frame = self.get_frame() self.add_frames(*[frame] * n_frames) - sleep(self.frame_duration * 99/100) + b = datetime.datetime.now() + time_diff = (b - a).total_seconds() + if time_diff < self.frame_duration: + sleep(self.frame_duration - time_diff) def clean_up_animations(self, *animations): for animation in animations: @@ -677,7 +677,11 @@ class Scene(Container): def tex(self, latex): eq = TextMobject(latex) - self.play(Write(eq)) + anims = [] + anims.append(Write(eq)) + for mobject in self.mobjects: + anims.append(ApplyMethod(mobject.shift,2*UP)) + self.play(*anims) class EndSceneEarlyException(Exception):