Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More customization #2

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
3 changes: 3 additions & 0 deletions manimlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
import manimlib.config
import manimlib.constants
import manimlib.extract_scene
import manimlib.utils.tex_file_writing


def main():
args = manimlib.config.parse_cli()
config = manimlib.config.get_configuration(args)
manimlib.constants.initialize_directories(config)
manimlib.constants.TEMPLATE_TEX_OBJ.configurate(config)
manimlib.constants.remember_presets(config)
manimlib.utils.tex_file_writing.clear_tex_cache(config)
manimlib.extract_scene.main(config)
2 changes: 1 addition & 1 deletion manimlib/animation/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def interpolate_submobject(self, submob, start_submob, alpha):
)

def get_bounds(self, alpha):
raise Exception("Not Implemented")
raise NotImplemented


class ShowCreation(ShowPartial):
Expand Down
12 changes: 12 additions & 0 deletions manimlib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ def parse_cli():
"file",
help="path to file holding the python code for the scene",
)
parser.add_argument(
"--presets",
default="manimlib/presets.conf",
help="File of configuration presets. manimlib/presets.conf by default."
)
parser.add_argument(
"scene_names",
nargs="*",
Expand Down Expand Up @@ -149,6 +154,11 @@ def parse_cli():
nargs="*",
help="Names of XeLaTeX which will be located at the beginning of the .tex preamble."
)
parser.add_argument(
"--clear-tex-cache",
action="store_true",
help="Recompile all .tex files before rendering."
)
return parser.parse_args()
except argparse.ArgumentError as err:
print(str(err))
Expand Down Expand Up @@ -211,6 +221,8 @@ def get_configuration(args):
"mono_font": args.mono_font,
"pre_packages": args.pre_packages,
"post_packages": args.post_packages,
"presets_file": args.presets,
"clear_tex_cache": args.clear_tex_cache
}

# Camera configuration
Expand Down
11 changes: 11 additions & 0 deletions manimlib/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
import os

from manimlib.utils.tex_preparing import TexFile
import configparser

MEDIA_DIR = ""
VIDEO_DIR = ""
VIDEO_OUTPUT_DIR = ""
TEX_DIR = ""
TEXT_DIR = ""

PRESETS = configparser.ConfigParser()


def initialize_directories(config):
global MEDIA_DIR
Expand Down Expand Up @@ -54,6 +57,14 @@ def initialize_directories(config):
if folder != "" and not os.path.exists(folder):
os.makedirs(folder)

def remember_presets(config):
global PRESETS
if not config["presets_file"]:
return

PRESETS.read_file(
open(config["presets_file"]))

NOT_SETTING_FONT_MSG='''
Warning:
You haven't set font.
Expand Down
11 changes: 6 additions & 5 deletions manimlib/mobject/coordinate_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@
from manimlib.mobject.number_line import NumberLine
from manimlib.mobject.svg.tex_mobject import TexMobject
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.utils.config_ops import digest_config
from manimlib.utils.config_ops import merge_dicts_recursively
from manimlib.utils.simple_functions import binary_search
from manimlib.utils.space_ops import angle_of_vector

from manimlib.utils.presetting import Presettable

# TODO: There should be much more code reuse between Axes, NumberPlane and GraphScene


class CoordinateSystem():
class CoordinateSystem(Presettable):
"""
Abstract class for Axes and NumberPlane
"""
Expand All @@ -29,10 +30,10 @@ class CoordinateSystem():
}

def coords_to_point(self, *coords):
raise Exception("Not implemented")
raise NotImplemented

def point_to_coords(self, point):
raise Exception("Not implemented")
raise NotImplemented

def c2p(self, *coords):
"""Abbreviation for coords_to_point"""
Expand All @@ -43,7 +44,7 @@ def p2c(self, point):
return self.point_to_coords(point)

def get_axes(self):
raise Exception("Not implemented")
raise NotImplemented

def get_axis(self, index):
return self.get_axes()[index]
Expand Down
4 changes: 2 additions & 2 deletions manimlib/mobject/mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ def get_start_and_end(self):
return self.get_start(), self.get_end()

def point_from_proportion(self, alpha):
raise Exception("Not implemented")
raise NotImplemented

def get_pieces(self, n_pieces):
template = self.copy()
Expand Down Expand Up @@ -1008,7 +1008,7 @@ def align_points(self, mobject):
return self

def align_points_with_larger(self, larger_mobject):
raise Exception("Not implemented")
raise NotImplemented

def align_submobjects(self, mobject):
mob1 = self
Expand Down
2 changes: 1 addition & 1 deletion manimlib/mobject/svg/svg_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def handle_command(self, command, coord_string):
# TODO, this is a suboptimal approximation
new_points = np.append([new_points[0]], new_points, axis=0)
elif command == "A": # elliptical Arc
raise Exception("Not implemented")
raise NotImplemented
elif command == "Z": # closepath
return

Expand Down
6 changes: 6 additions & 0 deletions manimlib/mobject/svg/tex_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ def break_up_by_substrings(self):
deeper based on the structure of tex_strings (as a list
of tex_strings)
"""
if not self.submobjects:
raise Exception(
f"TeX-compiled object \"{str(self.tex_string)}\" does not "
"contain any symbol. Consider using font compatible with your "
"language. Use -h option to get help on font parameters."
)
new_submobjects = []
curr_index = 0
config = dict(self.CONFIG)
Expand Down
2 changes: 1 addition & 1 deletion manimlib/mobject/types/image_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class AbstractImageMobject(Mobject):
}

def get_pixel_array(self):
raise Exception("Not implemented")
raise NotImplemented

def set_color(self):
# Likely to be implemented in subclasses, but no obgligation
Expand Down
8 changes: 4 additions & 4 deletions manimlib/once_useful_constructs/fractals.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ def get_order_n_self(self, order):
return result

def get_seed_shape(self):
raise Exception("Not implemented")
raise NotImplemented

def arrange_subparts(self, *subparts):
raise Exception("Not implemented")
raise NotImplemented


class Sierpinski(SelfSimilarFractal):
Expand Down Expand Up @@ -336,7 +336,7 @@ def init_colors(self):
self.set_stroke(width=self.order_to_stroke_width_map[order])

def get_anchor_points(self):
raise Exception("Not implemented")
raise NotImplemented


class LindenmayerCurve(FractalCurve):
Expand Down Expand Up @@ -421,7 +421,7 @@ def get_anchor_points(self):
return points

def generate_grid(self):
raise Exception("Not implemented")
raise NotImplemented


class HilbertCurve(SelfSimilarSpaceFillingCurve):
Expand Down
18 changes: 18 additions & 0 deletions manimlib/presets.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Use this file to make preset for every CONFIG of class that will be
# instantiated.
# Put the name of class into [brackets] and start setting properties in
# key=value format (just search for config file formatting).
# Nested parameters can be set by dot.

# Specify type of your value as "option_name is const", "... is str",
# "... is int" or "... is bool". Otherwise all values will be treated as string.
# The example is given below.

[NumberPlane]
background_line_style.stroke_color is const = RED

# Directives like that one can be easily used for styling your objects. Have a
# look at class codes in manimlib/mobject to know the class' properties.

[Mobject]
color is const = BLUE_D
5 changes: 2 additions & 3 deletions manimlib/utils/config_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,8 @@ def soft_dict_update(d1, d2):
Adds key values pairs of d2 to d1 only when d1 doesn't
already have that key
"""
for key, value in list(d2.items()):
if key not in d1:
d1[key] = value
for k, v in d2.items():
d1.setdefault(k, v)


def digest_locals(obj, keys=None):
Expand Down
68 changes: 68 additions & 0 deletions manimlib/utils/presetting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import re

from manimlib.constants import *


class Presettable:
"""Abstract class for classes which CONFIG may be preseted.
"""
CONFIG = {}
CONFIGURATED = False

def __new__(cls, *args, **kwargs):
if not cls.CONFIGURATED:
assign_presets(cls)
cls.CONFIGURATED = True
return object.__new__(cls, *args, **kwargs)


def change_by_dot(dictionary, key, value):
"""Access the dict value by dot key and change it to `value` Example:
dot_access({"a":0}, "a", ..) refers to d["a"]
dot_access({"a":{"b":0}}}, "a.b", ..) refers to d["a"]["b"]
"""

def replacer(dict_slice, value, key_slice, *rest_slices):
# if current key_slice is the last, rest_slices evaluates to None
if not rest_slices:
dict_slice[key_slice] = value
else:
replacer(dict_slice[key_slice], value, *rest_slices)

# recursively dive into dict, and if key was found in current nested item,
# change its value
replacer(dictionary, value, *key.split("."))


def assign_presets(obj):
"""Sets CONFIG values of the UNINSTANTIATED class as given in presets file
in manimlib/presets.conf.
"""

class_name = obj.__name__
if class_name not in PRESETS.sections():
return

# value parsers for different types should take two parameters:
# class name and option name and return the legit value for that property
value_parsers = {
"int": int,
"bool": lambda value: value.lower() in ["yes", "true", "1", "on"],
"str": str,
"const": lambda name: globals()[name]
}

for option in PRESETS.options(class_name):

if not option:
continue
# findall is guaranteed to match at least one symbol, so unpacking
# is safe.
prop_name, prop_type = re.findall(
r"([\w\d\.]+)(?: is (\w+))?",
option
)[0]
change_by_dot(
obj.CONFIG, prop_name,
value_parsers[prop_type or str](PRESETS.get(class_name, option))
)
16 changes: 16 additions & 0 deletions manimlib/utils/tex_file_writing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@
import manimlib.constants as consts


def clear_tex_cache(config):
"""Clear TEX_DIR path. May be useful in debugging or if you managed to
change .tex files there.
"""

if not config["clear_tex_cache"]:
return

for fname in os.listdir(consts.TEX_DIR):
fpath = os.path.join(consts.TEX_DIR, fname)
# user will see OSError here in case of some problems
if os.path.isfile(fpath):
os.unlink(fpath)


def tex_hash(expression, template_tex_file_body):
id_str = str(expression + template_tex_file_body)
hasher = hashlib.sha256()
Expand Down Expand Up @@ -32,6 +47,7 @@ def generate_tex_file(expression, template_tex_file_body):
outfile.write(template_tex_file_body)
return result


def tex_to_dvi(tex_file):
result = tex_file.replace(".tex", ".xdv")
result = Path(result).as_posix()
Expand Down
Loading