mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 06:22:54 +08:00
Fixes and refactoring for TexMobject
This commit is contained in:
@ -467,6 +467,8 @@ class Mobject(Container):
|
|||||||
|
|
||||||
# Background rectangle
|
# Background rectangle
|
||||||
def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs):
|
def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs):
|
||||||
|
# TODO, this does not behave well when the mobject has points,
|
||||||
|
# since it gets displayed on top
|
||||||
from mobject.shape_matchers import BackgroundRectangle
|
from mobject.shape_matchers import BackgroundRectangle
|
||||||
self.background_rectangle = BackgroundRectangle(
|
self.background_rectangle = BackgroundRectangle(
|
||||||
self, color=color,
|
self, color=color,
|
||||||
@ -742,8 +744,7 @@ class Mobject(Container):
|
|||||||
|
|
||||||
def submobject_family(self):
|
def submobject_family(self):
|
||||||
sub_families = map(Mobject.submobject_family, self.submobjects)
|
sub_families = map(Mobject.submobject_family, self.submobjects)
|
||||||
# all_mobjects = [self] + list(it.chain(*sub_families))
|
all_mobjects = [self] + list(it.chain(*sub_families))
|
||||||
all_mobjects = list(it.chain(*sub_families)) + [self]
|
|
||||||
return remove_list_redundancies(all_mobjects)
|
return remove_list_redundancies(all_mobjects)
|
||||||
|
|
||||||
def family_members_with_points(self):
|
def family_members_with_points(self):
|
||||||
|
@ -4,14 +4,12 @@ from svg_mobject import SVGMobject
|
|||||||
from svg_mobject import VMobjectFromSVGPathstring
|
from svg_mobject import VMobjectFromSVGPathstring
|
||||||
from utils.config_ops import digest_config
|
from utils.config_ops import digest_config
|
||||||
from utils.strings import split_string_list_to_isolate_substring
|
from utils.strings import split_string_list_to_isolate_substring
|
||||||
|
from utils.tex_file_writing import tex_to_svg_file
|
||||||
from mobject.types.vectorized_mobject import VGroup
|
from mobject.types.vectorized_mobject import VGroup
|
||||||
from mobject.types.vectorized_mobject import VectorizedPoint
|
from mobject.types.vectorized_mobject import VectorizedPoint
|
||||||
|
|
||||||
import operator as op
|
import operator as op
|
||||||
|
|
||||||
# TODO list
|
|
||||||
# - Make sure if "color" is passed into TexMobject, it behaves as expected
|
|
||||||
|
|
||||||
TEX_MOB_SCALE_FACTOR = 0.05
|
TEX_MOB_SCALE_FACTOR = 0.05
|
||||||
|
|
||||||
|
|
||||||
@ -67,11 +65,20 @@ class SingleStringTexMobject(SVGMobject):
|
|||||||
|
|
||||||
def modify_special_strings(self, tex):
|
def modify_special_strings(self, tex):
|
||||||
tex = self.remove_stray_braces(tex)
|
tex = self.remove_stray_braces(tex)
|
||||||
if tex in ["\\over", "\\overline"]:
|
should_add_filler = reduce(op.or_, [
|
||||||
# fraction line needs something to be over
|
# Fraction line needs something to be over
|
||||||
tex += "\\,"
|
tex == "\\over",
|
||||||
if tex == "\\sqrt":
|
tex == "\\overline",
|
||||||
tex += "{\\quad}"
|
# Makesure sqrt has overbar
|
||||||
|
tex == "\\sqrt",
|
||||||
|
# Need to add blank subscript or superscript
|
||||||
|
tex.endswith("_"),
|
||||||
|
tex.endswith("^"),
|
||||||
|
])
|
||||||
|
if should_add_filler:
|
||||||
|
filler = "{\\quad}"
|
||||||
|
tex += filler
|
||||||
|
|
||||||
if tex == "\\substack":
|
if tex == "\\substack":
|
||||||
tex = ""
|
tex = ""
|
||||||
for t1, t2 in ("\\left", "\\right"), ("\\right", "\\left"):
|
for t1, t2 in ("\\left", "\\right"), ("\\right", "\\left"):
|
||||||
@ -95,10 +102,9 @@ class SingleStringTexMobject(SVGMobject):
|
|||||||
for char in "{}"
|
for char in "{}"
|
||||||
]
|
]
|
||||||
if num_rights > num_lefts:
|
if num_rights > num_lefts:
|
||||||
backwards = tex[::-1].replace("}", "", num_rights - num_lefts)
|
tex = "{" + tex
|
||||||
tex = backwards[::-1]
|
|
||||||
elif num_lefts > num_rights:
|
elif num_lefts > num_rights:
|
||||||
tex = tex.replace("{", "", num_lefts - num_rights)
|
tex = tex + "}"
|
||||||
return tex
|
return tex
|
||||||
|
|
||||||
def get_tex_string(self):
|
def get_tex_string(self):
|
||||||
@ -273,98 +279,14 @@ class BulletedList(TextMobject):
|
|||||||
|
|
||||||
|
|
||||||
class TexMobjectFromPresetString(TexMobject):
|
class TexMobjectFromPresetString(TexMobject):
|
||||||
|
CONFIG = {
|
||||||
|
# To be filled by subclasses
|
||||||
|
"tex": None,
|
||||||
|
"color": None,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
TexMobject.__init__(self, self.tex, **kwargs)
|
TexMobject.__init__(self, self.tex, **kwargs)
|
||||||
self.set_color(self.color)
|
self.set_color(self.color)
|
||||||
|
|
||||||
##########
|
|
||||||
|
|
||||||
|
|
||||||
def tex_hash(expression, template_tex_file):
|
|
||||||
return str(hash(expression + template_tex_file))
|
|
||||||
|
|
||||||
|
|
||||||
def tex_to_svg_file(expression, template_tex_file):
|
|
||||||
image_dir = os.path.join(
|
|
||||||
TEX_IMAGE_DIR,
|
|
||||||
tex_hash(expression, template_tex_file)
|
|
||||||
)
|
|
||||||
if os.path.exists(image_dir):
|
|
||||||
return get_sorted_image_list(image_dir)
|
|
||||||
tex_file = generate_tex_file(expression, template_tex_file)
|
|
||||||
dvi_file = tex_to_dvi(tex_file)
|
|
||||||
return dvi_to_svg(dvi_file)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_tex_file(expression, template_tex_file):
|
|
||||||
result = os.path.join(
|
|
||||||
TEX_DIR,
|
|
||||||
tex_hash(expression, template_tex_file)
|
|
||||||
) + ".tex"
|
|
||||||
if not os.path.exists(result):
|
|
||||||
print("Writing \"%s\" to %s" % (
|
|
||||||
"".join(expression), result
|
|
||||||
))
|
|
||||||
with open(template_tex_file, "r") as infile:
|
|
||||||
body = infile.read()
|
|
||||||
body = body.replace(TEX_TEXT_TO_REPLACE, expression)
|
|
||||||
with open(result, "w") as outfile:
|
|
||||||
outfile.write(body)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def get_null():
|
|
||||||
if os.name == "nt":
|
|
||||||
return "NUL"
|
|
||||||
return "/dev/null"
|
|
||||||
|
|
||||||
|
|
||||||
def tex_to_dvi(tex_file):
|
|
||||||
result = tex_file.replace(".tex", ".dvi")
|
|
||||||
if not os.path.exists(result):
|
|
||||||
commands = [
|
|
||||||
"latex",
|
|
||||||
"-interaction=batchmode",
|
|
||||||
"-halt-on-error",
|
|
||||||
"-output-directory=" + TEX_DIR,
|
|
||||||
tex_file,
|
|
||||||
">",
|
|
||||||
get_null()
|
|
||||||
]
|
|
||||||
exit_code = os.system(" ".join(commands))
|
|
||||||
if exit_code != 0:
|
|
||||||
latex_output = ''
|
|
||||||
log_file = tex_file.replace(".tex", ".log")
|
|
||||||
if os.path.exists(log_file):
|
|
||||||
with open(log_file, 'r') as f:
|
|
||||||
latex_output = f.read()
|
|
||||||
raise Exception(
|
|
||||||
"Latex error converting to dvi. "
|
|
||||||
"See log output above or the log file: %s" % log_file)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def dvi_to_svg(dvi_file, regen_if_exists=False):
|
|
||||||
"""
|
|
||||||
Converts a dvi, which potentially has multiple slides, into a
|
|
||||||
directory full of enumerated pngs corresponding with these slides.
|
|
||||||
Returns a list of PIL Image objects for these images sorted as they
|
|
||||||
where in the dvi
|
|
||||||
"""
|
|
||||||
result = dvi_file.replace(".dvi", ".svg")
|
|
||||||
if not os.path.exists(result):
|
|
||||||
commands = [
|
|
||||||
"dvisvgm",
|
|
||||||
dvi_file,
|
|
||||||
"-n",
|
|
||||||
"-v",
|
|
||||||
"0",
|
|
||||||
"-o",
|
|
||||||
result,
|
|
||||||
">",
|
|
||||||
get_null()
|
|
||||||
]
|
|
||||||
os.system(" ".join(commands))
|
|
||||||
return result
|
|
||||||
|
81
utils/tex_file_writing.py
Normal file
81
utils/tex_file_writing.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import os
|
||||||
|
from constants import TEX_DIR
|
||||||
|
from constants import TEX_TEXT_TO_REPLACE
|
||||||
|
|
||||||
|
|
||||||
|
def tex_hash(expression, template_tex_file):
|
||||||
|
return str(hash(expression + template_tex_file))
|
||||||
|
|
||||||
|
|
||||||
|
def tex_to_svg_file(expression, template_tex_file):
|
||||||
|
tex_file = generate_tex_file(expression, template_tex_file)
|
||||||
|
dvi_file = tex_to_dvi(tex_file)
|
||||||
|
return dvi_to_svg(dvi_file)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_tex_file(expression, template_tex_file):
|
||||||
|
result = os.path.join(
|
||||||
|
TEX_DIR,
|
||||||
|
tex_hash(expression, template_tex_file)
|
||||||
|
) + ".tex"
|
||||||
|
if not os.path.exists(result):
|
||||||
|
print("Writing \"%s\" to %s" % (
|
||||||
|
"".join(expression), result
|
||||||
|
))
|
||||||
|
with open(template_tex_file, "r") as infile:
|
||||||
|
body = infile.read()
|
||||||
|
body = body.replace(TEX_TEXT_TO_REPLACE, expression)
|
||||||
|
with open(result, "w") as outfile:
|
||||||
|
outfile.write(body)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_null():
|
||||||
|
if os.name == "nt":
|
||||||
|
return "NUL"
|
||||||
|
return "/dev/null"
|
||||||
|
|
||||||
|
|
||||||
|
def tex_to_dvi(tex_file):
|
||||||
|
result = tex_file.replace(".tex", ".dvi")
|
||||||
|
if not os.path.exists(result):
|
||||||
|
commands = [
|
||||||
|
"latex",
|
||||||
|
"-interaction=batchmode",
|
||||||
|
"-halt-on-error",
|
||||||
|
"-output-directory=" + TEX_DIR,
|
||||||
|
tex_file,
|
||||||
|
">",
|
||||||
|
get_null()
|
||||||
|
]
|
||||||
|
exit_code = os.system(" ".join(commands))
|
||||||
|
if exit_code != 0:
|
||||||
|
log_file = tex_file.replace(".tex", ".log")
|
||||||
|
raise Exception(
|
||||||
|
"Latex error converting to dvi. "
|
||||||
|
"See log output above or the log file: %s" % log_file)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def dvi_to_svg(dvi_file, regen_if_exists=False):
|
||||||
|
"""
|
||||||
|
Converts a dvi, which potentially has multiple slides, into a
|
||||||
|
directory full of enumerated pngs corresponding with these slides.
|
||||||
|
Returns a list of PIL Image objects for these images sorted as they
|
||||||
|
where in the dvi
|
||||||
|
"""
|
||||||
|
result = dvi_file.replace(".dvi", ".svg")
|
||||||
|
if not os.path.exists(result):
|
||||||
|
commands = [
|
||||||
|
"dvisvgm",
|
||||||
|
dvi_file,
|
||||||
|
"-n",
|
||||||
|
"-v",
|
||||||
|
"0",
|
||||||
|
"-o",
|
||||||
|
result,
|
||||||
|
">",
|
||||||
|
get_null()
|
||||||
|
]
|
||||||
|
os.system(" ".join(commands))
|
||||||
|
return result
|
Reference in New Issue
Block a user