mirror of
https://github.com/3b1b/manim.git
synced 2025-07-27 20:12:24 +08:00

Temp files are project specific and thus can stay in the current project directory. Also breaks things if the package is installed in a read-only location. Which could be expected because you don't generally expect the program directories to be modified (except by the package manager).
243 lines
6.6 KiB
Python
243 lines
6.6 KiB
Python
import numpy as np
|
|
import os
|
|
|
|
# Initialize directories
|
|
env_MEDIA_DIR = os.getenv("MEDIA_DIR")
|
|
if env_MEDIA_DIR:
|
|
MEDIA_DIR = env_MEDIA_DIR
|
|
elif os.path.isfile("media_dir.txt"):
|
|
with open("media_dir.txt", 'rU') as media_file:
|
|
MEDIA_DIR = media_file.readline().strip()
|
|
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 stored in {MEDIA_DIR + os.sep}. You can change "
|
|
"this behavior by writing a different directory to media_dir.txt."
|
|
)
|
|
|
|
VIDEO_DIR = os.path.join(MEDIA_DIR, "videos")
|
|
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
|
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
|
SOUND_DIR = os.path.join(MEDIA_DIR, "designs", "sounds")
|
|
###
|
|
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
|
FILE_DIR = os.path.join(os.getenv("FILE_DIR", default="."), "files")
|
|
TEX_DIR = os.path.join(FILE_DIR, "Tex")
|
|
# These two may be depricated now.
|
|
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
|
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
|
|
|
for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, VIDEO_DIR,
|
|
TEX_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR]:
|
|
if not os.path.exists(folder):
|
|
os.makedirs(folder)
|
|
|
|
TEX_USE_CTEX = False
|
|
TEX_TEXT_TO_REPLACE = "YourTextHere"
|
|
TEMPLATE_TEX_FILE = os.path.join(
|
|
THIS_DIR, "tex_template.tex" if not TEX_USE_CTEX
|
|
else "ctex_template.tex"
|
|
)
|
|
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 = """
|
|
{} is not in the script
|
|
"""
|
|
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
|
|
"""
|
|
|
|
# There might be other configuration than pixel shape later...
|
|
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
|
"pixel_height": 1440,
|
|
"pixel_width": 2560,
|
|
"frame_rate": 60,
|
|
}
|
|
|
|
HIGH_QUALITY_CAMERA_CONFIG = {
|
|
"pixel_height": 1080,
|
|
"pixel_width": 1920,
|
|
"frame_rate": 30,
|
|
}
|
|
|
|
MEDIUM_QUALITY_CAMERA_CONFIG = {
|
|
"pixel_height": 720,
|
|
"pixel_width": 1280,
|
|
"frame_rate": 30,
|
|
}
|
|
|
|
LOW_QUALITY_CAMERA_CONFIG = {
|
|
"pixel_height": 480,
|
|
"pixel_width": 854,
|
|
"frame_rate": 15,
|
|
}
|
|
|
|
DEFAULT_PIXEL_HEIGHT = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"]
|
|
DEFAULT_PIXEL_WIDTH = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"]
|
|
DEFAULT_FRAME_RATE = 60
|
|
|
|
DEFAULT_POINT_DENSITY_2D = 25
|
|
DEFAULT_POINT_DENSITY_1D = 250
|
|
|
|
DEFAULT_STROKE_WIDTH = 4
|
|
|
|
FRAME_HEIGHT = 8.0
|
|
FRAME_WIDTH = FRAME_HEIGHT * DEFAULT_PIXEL_WIDTH / DEFAULT_PIXEL_HEIGHT
|
|
FRAME_Y_RADIUS = FRAME_HEIGHT / 2
|
|
FRAME_X_RADIUS = FRAME_WIDTH / 2
|
|
|
|
SMALL_BUFF = 0.1
|
|
MED_SMALL_BUFF = 0.25
|
|
MED_LARGE_BUFF = 0.5
|
|
LARGE_BUFF = 1
|
|
|
|
DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF
|
|
DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF
|
|
|
|
|
|
# All in seconds
|
|
DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0
|
|
DEFAULT_WAIT_TIME = 1.0
|
|
|
|
|
|
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.))
|
|
|
|
# Useful abbreviations for diagonals
|
|
UL = UP + LEFT
|
|
UR = UP + RIGHT
|
|
DL = DOWN + LEFT
|
|
DR = DOWN + RIGHT
|
|
|
|
TOP = FRAME_Y_RADIUS * UP
|
|
BOTTOM = FRAME_Y_RADIUS * DOWN
|
|
LEFT_SIDE = FRAME_X_RADIUS * LEFT
|
|
RIGHT_SIDE = FRAME_X_RADIUS * RIGHT
|
|
|
|
PI = np.pi
|
|
TAU = 2 * PI
|
|
DEGREES = TAU / 360
|
|
|
|
FFMPEG_BIN = "ffmpeg"
|
|
|
|
# Colors
|
|
COLOR_MAP = {
|
|
"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",
|
|
"DARKER_GREY": "#222222",
|
|
"DARKER_GRAY": "#222222",
|
|
"GREY_BROWN": "#736357",
|
|
"PINK": "#D147BD",
|
|
"GREEN_SCREEN": "#00FF00",
|
|
"ORANGE": "#FF862F",
|
|
}
|
|
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]
|
|
|
|
# Streaming related configuration
|
|
LIVE_STREAM_NAME = "LiveStream"
|
|
TWITCH_STREAM_KEY = "YOUR_STREAM_KEY"
|
|
STREAMING_PROTOCOL = "tcp"
|
|
STREAMING_IP = "127.0.0.1"
|
|
STREAMING_PORT = "2000"
|
|
STREAMING_CLIENT = "ffplay"
|
|
STREAMING_URL = f"{STREAMING_PROTOCOL}://{STREAMING_IP}:{STREAMING_PORT}?listen"
|
|
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))
|
|
"""
|