2015-06-19 08:31:02 -07:00
|
|
|
import numpy as np
|
2018-12-24 12:37:51 -08:00
|
|
|
import os
|
2015-03-22 13:33:02 -06:00
|
|
|
|
2019-06-04 20:51:18 -07:00
|
|
|
MEDIA_DIR = ""
|
|
|
|
VIDEO_DIR = ""
|
2019-06-21 22:52:16 -07:00
|
|
|
VIDEO_OUTPUT_DIR = ""
|
2019-06-04 20:51:18 -07:00
|
|
|
TEX_DIR = ""
|
2019-08-05 22:53:15 +08:00
|
|
|
TEXT_DIR = ""
|
2018-03-30 18:19:23 -07:00
|
|
|
|
2019-07-01 12:51:48 -07:00
|
|
|
|
2019-06-04 20:51:18 -07:00
|
|
|
def initialize_directories(config):
|
|
|
|
global MEDIA_DIR
|
|
|
|
global VIDEO_DIR
|
2019-06-21 22:52:16 -07:00
|
|
|
global VIDEO_OUTPUT_DIR
|
2019-06-04 20:51:18 -07:00
|
|
|
global TEX_DIR
|
2019-08-05 22:53:15 +08:00
|
|
|
global TEXT_DIR
|
2019-06-21 22:52:16 -07:00
|
|
|
|
|
|
|
video_path_specified = config["video_dir"] or config["video_output_dir"]
|
|
|
|
|
|
|
|
if not (video_path_specified and config["tex_dir"]):
|
2019-06-04 20:51:18 -07:00
|
|
|
if config["media_dir"]:
|
|
|
|
MEDIA_DIR = config["media_dir"]
|
|
|
|
else:
|
|
|
|
MEDIA_DIR = os.path.join(
|
|
|
|
os.path.expanduser('~'),
|
|
|
|
"Dropbox (3Blue1Brown)/3Blue1Brown Team Folder"
|
|
|
|
)
|
|
|
|
if not os.path.isdir(MEDIA_DIR):
|
|
|
|
MEDIA_DIR = "./media"
|
|
|
|
print(
|
|
|
|
f"Media will be written to {MEDIA_DIR + os.sep}. You can change "
|
|
|
|
"this behavior with the --media_dir flag."
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
if config["media_dir"]:
|
|
|
|
print(
|
2019-06-21 22:52:16 -07:00
|
|
|
"Ignoring --media_dir, since both --tex_dir and a video "
|
|
|
|
"directory were both passed"
|
2019-06-04 20:51:18 -07:00
|
|
|
)
|
2018-12-22 14:27:22 -08:00
|
|
|
|
2019-08-16 15:53:36 +08:00
|
|
|
TEX_DIR = config["tex_dir"] or os.path.join(MEDIA_DIR, "Tex")
|
2019-08-05 22:53:15 +08:00
|
|
|
TEXT_DIR = os.path.join(MEDIA_DIR, "texts")
|
2019-07-01 12:51:48 -07:00
|
|
|
if not video_path_specified:
|
|
|
|
VIDEO_DIR = os.path.join(MEDIA_DIR, "videos")
|
|
|
|
VIDEO_OUTPUT_DIR = os.path.join(MEDIA_DIR, "videos")
|
|
|
|
elif config["video_output_dir"]:
|
|
|
|
VIDEO_OUTPUT_DIR = config["video_output_dir"]
|
|
|
|
else:
|
|
|
|
VIDEO_DIR = config["video_dir"]
|
|
|
|
|
2019-08-05 22:53:15 +08:00
|
|
|
for folder in [VIDEO_DIR, VIDEO_OUTPUT_DIR, TEX_DIR, TEXT_DIR]:
|
2019-06-21 22:52:16 -07:00
|
|
|
if folder != "" and not os.path.exists(folder):
|
2019-06-04 20:51:18 -07:00
|
|
|
os.makedirs(folder)
|
2018-12-22 14:27:22 -08:00
|
|
|
|
2019-08-05 22:53:15 +08:00
|
|
|
NOT_SETTING_FONT_MSG='''
|
|
|
|
Warning:
|
2019-08-12 09:35:05 +08:00
|
|
|
You haven't set font.
|
2019-08-05 22:53:15 +08:00
|
|
|
If you are not using English, this may cause text rendering problem.
|
2019-08-12 09:35:05 +08:00
|
|
|
You set font like:
|
|
|
|
text = Text('your text', font='your font')
|
|
|
|
or:
|
|
|
|
class MyText(Text):
|
|
|
|
CONFIG = {
|
|
|
|
'font': 'My Font'
|
|
|
|
}
|
2019-08-05 22:53:15 +08:00
|
|
|
'''
|
2019-08-12 09:35:05 +08:00
|
|
|
START_X = 30
|
|
|
|
START_Y = 20
|
2019-08-05 22:53:15 +08:00
|
|
|
NORMAL = 'NORMAL'
|
|
|
|
ITALIC = 'ITALIC'
|
|
|
|
OBLIQUE = 'OBLIQUE'
|
|
|
|
BOLD = 'BOLD'
|
2019-07-01 12:51:48 -07:00
|
|
|
|
2018-12-22 14:27:22 -08:00
|
|
|
TEX_USE_CTEX = False
|
|
|
|
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
|
|
|
TEMPLATE_TEX_FILE = os.path.join(
|
2019-06-03 23:41:05 -07:00
|
|
|
os.path.dirname(os.path.realpath(__file__)),
|
|
|
|
"tex_template.tex" if not TEX_USE_CTEX else "ctex_template.tex"
|
2018-12-22 14:27:22 -08:00
|
|
|
)
|
|
|
|
with open(TEMPLATE_TEX_FILE, "r") as infile:
|
|
|
|
TEMPLATE_TEXT_FILE_BODY = infile.read()
|
|
|
|
TEMPLATE_TEX_FILE_BODY = TEMPLATE_TEXT_FILE_BODY.replace(
|
|
|
|
TEX_TEXT_TO_REPLACE,
|
|
|
|
"\\begin{align*}\n" + TEX_TEXT_TO_REPLACE + "\n\\end{align*}",
|
|
|
|
)
|
|
|
|
|
|
|
|
HELP_MESSAGE = """
|
|
|
|
Usage:
|
|
|
|
python extract_scene.py <module> [<scene name>]
|
|
|
|
-p preview in low quality
|
|
|
|
-s show and save picture of last frame
|
|
|
|
-w write result to file [this is default if nothing else is stated]
|
|
|
|
-o <file_name> write to a different file_name
|
|
|
|
-l use low quality
|
|
|
|
-m use medium quality
|
|
|
|
-a run and save every scene in the script, or all args for the given scene
|
|
|
|
-q don't print progress
|
|
|
|
-f when writing to a movie file, export the frames in png sequence
|
|
|
|
-t use transperency when exporting images
|
|
|
|
-n specify the number of the animation to start from
|
|
|
|
-r specify a resolution
|
|
|
|
-c specify a background color
|
|
|
|
"""
|
|
|
|
SCENE_NOT_FOUND_MESSAGE = """
|
2019-01-12 12:31:29 -08:00
|
|
|
{} is not in the script
|
2018-12-22 14:27:22 -08:00
|
|
|
"""
|
|
|
|
CHOOSE_NUMBER_MESSAGE = """
|
|
|
|
Choose number corresponding to desired scene/arguments.
|
|
|
|
(Use comma separated list for multiple entries)
|
|
|
|
Choice(s): """
|
|
|
|
INVALID_NUMBER_MESSAGE = "Fine then, if you don't want to give a valid number I'll just quit"
|
|
|
|
|
|
|
|
NO_SCENE_MESSAGE = """
|
|
|
|
There are no scenes inside that module
|
|
|
|
"""
|
|
|
|
|
2018-05-21 12:11:46 -07:00
|
|
|
# There might be other configuration than pixel shape later...
|
2016-03-07 19:07:00 -08:00
|
|
|
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
2018-07-31 19:39:17 -07:00
|
|
|
"pixel_height": 1440,
|
|
|
|
"pixel_width": 2560,
|
2019-01-25 10:13:17 -08:00
|
|
|
"frame_rate": 60,
|
2018-07-31 19:39:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
HIGH_QUALITY_CAMERA_CONFIG = {
|
|
|
|
"pixel_height": 1080,
|
|
|
|
"pixel_width": 1920,
|
2019-06-02 18:58:33 -07:00
|
|
|
"frame_rate": 60,
|
2015-05-07 21:28:02 -07:00
|
|
|
}
|
|
|
|
|
2016-03-07 19:07:00 -08:00
|
|
|
MEDIUM_QUALITY_CAMERA_CONFIG = {
|
2018-05-21 12:11:46 -07:00
|
|
|
"pixel_height": 720,
|
|
|
|
"pixel_width": 1280,
|
2019-01-25 10:13:17 -08:00
|
|
|
"frame_rate": 30,
|
2015-10-20 21:55:46 -07:00
|
|
|
}
|
|
|
|
|
2016-03-07 19:07:00 -08:00
|
|
|
LOW_QUALITY_CAMERA_CONFIG = {
|
2018-05-21 12:11:46 -07:00
|
|
|
"pixel_height": 480,
|
|
|
|
"pixel_width": 854,
|
2019-01-25 10:13:17 -08:00
|
|
|
"frame_rate": 15,
|
2015-05-07 21:28:02 -07:00
|
|
|
}
|
|
|
|
|
2018-07-31 19:39:17 -07:00
|
|
|
DEFAULT_PIXEL_HEIGHT = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"]
|
|
|
|
DEFAULT_PIXEL_WIDTH = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"]
|
2019-02-03 12:10:00 -08:00
|
|
|
DEFAULT_FRAME_RATE = 60
|
2018-07-31 19:39:17 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
DEFAULT_POINT_DENSITY_2D = 25
|
2016-03-21 19:30:36 -07:00
|
|
|
DEFAULT_POINT_DENSITY_1D = 250
|
2015-05-07 21:28:02 -07:00
|
|
|
|
2018-08-12 11:42:59 -07:00
|
|
|
DEFAULT_STROKE_WIDTH = 4
|
2015-10-10 18:48:25 -07:00
|
|
|
|
2018-03-30 11:25:37 -07:00
|
|
|
FRAME_HEIGHT = 8.0
|
2018-03-30 11:36:06 -07:00
|
|
|
FRAME_WIDTH = FRAME_HEIGHT * DEFAULT_PIXEL_WIDTH / DEFAULT_PIXEL_HEIGHT
|
2018-04-06 13:58:59 -07:00
|
|
|
FRAME_Y_RADIUS = FRAME_HEIGHT / 2
|
|
|
|
FRAME_X_RADIUS = FRAME_WIDTH / 2
|
2015-06-19 08:31:02 -07:00
|
|
|
|
2016-08-17 16:14:15 -07:00
|
|
|
SMALL_BUFF = 0.1
|
2017-01-25 16:40:59 -08:00
|
|
|
MED_SMALL_BUFF = 0.25
|
|
|
|
MED_LARGE_BUFF = 0.5
|
2016-08-17 16:14:15 -07:00
|
|
|
LARGE_BUFF = 1
|
|
|
|
|
2017-01-25 16:40:59 -08:00
|
|
|
DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF
|
|
|
|
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF
|
2015-09-28 16:25:18 -07:00
|
|
|
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
# All in seconds
|
2015-10-20 21:55:46 -07:00
|
|
|
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
|
2018-01-17 21:32:50 -08:00
|
|
|
DEFAULT_WAIT_TIME = 1.0
|
2015-03-22 13:33:02 -06:00
|
|
|
|
2015-06-19 08:31:02 -07:00
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
ORIGIN = np.array((0., 0., 0.))
|
|
|
|
UP = np.array((0., 1., 0.))
|
|
|
|
DOWN = np.array((0., -1., 0.))
|
|
|
|
RIGHT = np.array((1., 0., 0.))
|
|
|
|
LEFT = np.array((-1., 0., 0.))
|
|
|
|
IN = np.array((0., 0., -1.))
|
|
|
|
OUT = np.array((0., 0., 1.))
|
|
|
|
X_AXIS = np.array((1., 0., 0.))
|
|
|
|
Y_AXIS = np.array((0., 1., 0.))
|
|
|
|
Z_AXIS = np.array((0., 0., 1.))
|
2015-03-26 22:49:22 -06:00
|
|
|
|
2018-04-09 16:17:02 -07:00
|
|
|
# Useful abbreviations for diagonals
|
|
|
|
UL = UP + LEFT
|
|
|
|
UR = UP + RIGHT
|
|
|
|
DL = DOWN + LEFT
|
|
|
|
DR = DOWN + RIGHT
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
TOP = FRAME_Y_RADIUS * UP
|
|
|
|
BOTTOM = FRAME_Y_RADIUS * DOWN
|
|
|
|
LEFT_SIDE = FRAME_X_RADIUS * LEFT
|
|
|
|
RIGHT_SIDE = FRAME_X_RADIUS * RIGHT
|
2015-10-09 19:53:38 -07:00
|
|
|
|
2018-07-16 19:42:06 -07:00
|
|
|
PI = np.pi
|
|
|
|
TAU = 2 * PI
|
2018-04-06 13:58:59 -07:00
|
|
|
DEGREES = TAU / 360
|
2018-01-16 09:43:05 -08:00
|
|
|
|
2016-02-23 22:29:32 -08:00
|
|
|
FFMPEG_BIN = "ffmpeg"
|
|
|
|
|
2018-04-06 13:58:59 -07:00
|
|
|
# Colors
|
2015-10-20 21:55:46 -07:00
|
|
|
COLOR_MAP = {
|
2018-04-06 13:58:59 -07:00
|
|
|
"DARK_BLUE": "#236B8E",
|
|
|
|
"DARK_BROWN": "#8B4513",
|
|
|
|
"LIGHT_BROWN": "#CD853F",
|
|
|
|
"BLUE_E": "#1C758A",
|
|
|
|
"BLUE_D": "#29ABCA",
|
|
|
|
"BLUE_C": "#58C4DD",
|
|
|
|
"BLUE_B": "#9CDCEB",
|
|
|
|
"BLUE_A": "#C7E9F1",
|
|
|
|
"TEAL_E": "#49A88F",
|
|
|
|
"TEAL_D": "#55C1A7",
|
|
|
|
"TEAL_C": "#5CD0B3",
|
|
|
|
"TEAL_B": "#76DDC0",
|
|
|
|
"TEAL_A": "#ACEAD7",
|
|
|
|
"GREEN_E": "#699C52",
|
|
|
|
"GREEN_D": "#77B05D",
|
|
|
|
"GREEN_C": "#83C167",
|
|
|
|
"GREEN_B": "#A6CF8C",
|
|
|
|
"GREEN_A": "#C9E2AE",
|
|
|
|
"YELLOW_E": "#E8C11C",
|
|
|
|
"YELLOW_D": "#F4D345",
|
|
|
|
"YELLOW_C": "#FFFF00",
|
|
|
|
"YELLOW_B": "#FFEA94",
|
|
|
|
"YELLOW_A": "#FFF1B6",
|
|
|
|
"GOLD_E": "#C78D46",
|
|
|
|
"GOLD_D": "#E1A158",
|
|
|
|
"GOLD_C": "#F0AC5F",
|
|
|
|
"GOLD_B": "#F9B775",
|
|
|
|
"GOLD_A": "#F7C797",
|
|
|
|
"RED_E": "#CF5044",
|
|
|
|
"RED_D": "#E65A4C",
|
|
|
|
"RED_C": "#FC6255",
|
|
|
|
"RED_B": "#FF8080",
|
|
|
|
"RED_A": "#F7A1A3",
|
|
|
|
"MAROON_E": "#94424F",
|
|
|
|
"MAROON_D": "#A24D61",
|
|
|
|
"MAROON_C": "#C55F73",
|
|
|
|
"MAROON_B": "#EC92AB",
|
|
|
|
"MAROON_A": "#ECABC1",
|
|
|
|
"PURPLE_E": "#644172",
|
|
|
|
"PURPLE_D": "#715582",
|
|
|
|
"PURPLE_C": "#9A72AC",
|
|
|
|
"PURPLE_B": "#B189C6",
|
|
|
|
"PURPLE_A": "#CAA3E8",
|
|
|
|
"WHITE": "#FFFFFF",
|
|
|
|
"BLACK": "#000000",
|
|
|
|
"LIGHT_GRAY": "#BBBBBB",
|
|
|
|
"LIGHT_GREY": "#BBBBBB",
|
|
|
|
"GRAY": "#888888",
|
|
|
|
"GREY": "#888888",
|
|
|
|
"DARK_GREY": "#444444",
|
|
|
|
"DARK_GRAY": "#444444",
|
2019-03-20 17:37:57 -07:00
|
|
|
"DARKER_GREY": "#222222",
|
|
|
|
"DARKER_GRAY": "#222222",
|
2018-04-06 13:58:59 -07:00
|
|
|
"GREY_BROWN": "#736357",
|
|
|
|
"PINK": "#D147BD",
|
2019-06-29 15:19:43 -07:00
|
|
|
"LIGHT_PINK": "#DC75CD",
|
2016-07-13 22:03:31 -07:00
|
|
|
"GREEN_SCREEN": "#00FF00",
|
2018-04-06 13:58:59 -07:00
|
|
|
"ORANGE": "#FF862F",
|
2015-10-20 21:55:46 -07:00
|
|
|
}
|
2018-08-09 17:56:05 -07:00
|
|
|
PALETTE = list(COLOR_MAP.values())
|
2016-07-27 13:10:45 -07:00
|
|
|
locals().update(COLOR_MAP)
|
2018-08-09 17:56:05 -07:00
|
|
|
for name in [s for s in list(COLOR_MAP.keys()) if s.endswith("_C")]:
|
2016-07-27 13:10:45 -07:00
|
|
|
locals()[name.replace("_C", "")] = locals()[name]
|
2018-10-30 18:30:34 +03:00
|
|
|
|
2018-12-22 14:27:22 -08:00
|
|
|
# Streaming related configuration
|
2018-10-30 18:30:34 +03:00
|
|
|
LIVE_STREAM_NAME = "LiveStream"
|
2018-10-31 21:29:01 +03:00
|
|
|
TWITCH_STREAM_KEY = "YOUR_STREAM_KEY"
|
|
|
|
STREAMING_PROTOCOL = "tcp"
|
|
|
|
STREAMING_IP = "127.0.0.1"
|
|
|
|
STREAMING_PORT = "2000"
|
|
|
|
STREAMING_CLIENT = "ffplay"
|
2018-12-22 14:27:22 -08:00
|
|
|
STREAMING_URL = f"{STREAMING_PROTOCOL}://{STREAMING_IP}:{STREAMING_PORT}?listen"
|
2018-12-22 18:25:35 -08:00
|
|
|
STREAMING_CONSOLE_BANNER = """
|
|
|
|
Manim is now running in streaming mode. Stream animations by passing
|
|
|
|
them to manim.play(), e.g.
|
|
|
|
>>> c = Circle()
|
|
|
|
>>> manim.play(ShowCreation(c))
|
|
|
|
"""
|