mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Make caching on disk a decorator, and update implementations for Tex and Text mobjects
This commit is contained in:
parent
89ddfadf6b
commit
43821ab2ba
6 changed files with 104 additions and 74 deletions
|
@ -77,11 +77,7 @@ class SingleStringTex(SVGMobject):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_svg_string_by_content(self, content: str) -> str:
|
def get_svg_string_by_content(self, content: str) -> str:
|
||||||
return get_cached_value(
|
return latex_to_svg(content, self.template, self.additional_preamble)
|
||||||
key=hash_string(str((content, self.template, self.additional_preamble))),
|
|
||||||
value_func=lambda: latex_to_svg(content, self.template, self.additional_preamble),
|
|
||||||
message=f"Writing {self.tex_string}..."
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tex_file_body(self, tex_string: str) -> str:
|
def get_tex_file_body(self, tex_string: str) -> str:
|
||||||
new_tex = self.get_modified_expression(tex_string)
|
new_tex = self.get_modified_expression(tex_string)
|
||||||
|
|
|
@ -6,7 +6,6 @@ from pathlib import Path
|
||||||
from manimlib.mobject.svg.string_mobject import StringMobject
|
from manimlib.mobject.svg.string_mobject import StringMobject
|
||||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||||
from manimlib.utils.cache import get_cached_value
|
|
||||||
from manimlib.utils.color import color_to_hex
|
from manimlib.utils.color import color_to_hex
|
||||||
from manimlib.utils.color import hex_to_int
|
from manimlib.utils.color import hex_to_int
|
||||||
from manimlib.utils.tex_file_writing import latex_to_svg
|
from manimlib.utils.tex_file_writing import latex_to_svg
|
||||||
|
@ -86,11 +85,7 @@ class Tex(StringMobject):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_svg_string_by_content(self, content: str) -> str:
|
def get_svg_string_by_content(self, content: str) -> str:
|
||||||
return get_cached_value(
|
return latex_to_svg(content, self.template, self.additional_preamble, short_tex=self.tex_string)
|
||||||
key=hash_string(str((content, self.template, self.additional_preamble))),
|
|
||||||
value_func=lambda: latex_to_svg(content, self.template, self.additional_preamble),
|
|
||||||
message=f"Writing {self.tex_string}..."
|
|
||||||
)
|
|
||||||
|
|
||||||
def _handle_scale_side_effects(self, scale_factor: float) -> Self:
|
def _handle_scale_side_effects(self, scale_factor: float) -> Self:
|
||||||
self.font_size *= scale_factor
|
self.font_size *= scale_factor
|
||||||
|
|
|
@ -16,7 +16,7 @@ from manimlib.constants import DEFAULT_PIXEL_WIDTH, FRAME_WIDTH
|
||||||
from manimlib.constants import NORMAL
|
from manimlib.constants import NORMAL
|
||||||
from manimlib.logger import log
|
from manimlib.logger import log
|
||||||
from manimlib.mobject.svg.string_mobject import StringMobject
|
from manimlib.mobject.svg.string_mobject import StringMobject
|
||||||
from manimlib.utils.cache import get_cached_value
|
from manimlib.utils.cache import cache_on_disk
|
||||||
from manimlib.utils.color import color_to_hex
|
from manimlib.utils.color import color_to_hex
|
||||||
from manimlib.utils.color import int_to_hex
|
from manimlib.utils.color import int_to_hex
|
||||||
from manimlib.utils.customization import get_customization
|
from manimlib.utils.customization import get_customization
|
||||||
|
@ -51,6 +51,57 @@ class _Alignment:
|
||||||
self.value = _Alignment.VAL_DICT[s.upper()]
|
self.value = _Alignment.VAL_DICT[s.upper()]
|
||||||
|
|
||||||
|
|
||||||
|
@cache_on_disk
|
||||||
|
def markup_to_svg_string(
|
||||||
|
markup_str: str,
|
||||||
|
justify: bool = False,
|
||||||
|
indent: float = 0,
|
||||||
|
alignment: str = "",
|
||||||
|
line_width: float | None = None,
|
||||||
|
) -> str:
|
||||||
|
validate_error = manimpango.MarkupUtils.validate(markup_str)
|
||||||
|
if validate_error:
|
||||||
|
raise ValueError(
|
||||||
|
f"Invalid markup string \"{markup_str}\"\n" + \
|
||||||
|
f"{validate_error}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# `manimpango` is under construction,
|
||||||
|
# so the following code is intended to suit its interface
|
||||||
|
alignment = _Alignment(alignment)
|
||||||
|
if line_width is None:
|
||||||
|
pango_width = -1
|
||||||
|
else:
|
||||||
|
pango_width = line_width / FRAME_WIDTH * DEFAULT_PIXEL_WIDTH
|
||||||
|
|
||||||
|
# Write the result to a temporary svg file, and return it's contents.
|
||||||
|
# TODO, better would be to have this not write to file at all
|
||||||
|
with tempfile.NamedTemporaryFile(suffix='.svg', mode='r+') as tmp:
|
||||||
|
manimpango.MarkupUtils.text2svg(
|
||||||
|
text=markup_str,
|
||||||
|
font="", # Already handled
|
||||||
|
slant="NORMAL", # Already handled
|
||||||
|
weight="NORMAL", # Already handled
|
||||||
|
size=1, # Already handled
|
||||||
|
_=0, # Empty parameter
|
||||||
|
disable_liga=False,
|
||||||
|
file_name=tmp.name,
|
||||||
|
START_X=0,
|
||||||
|
START_Y=0,
|
||||||
|
width=DEFAULT_CANVAS_WIDTH,
|
||||||
|
height=DEFAULT_CANVAS_HEIGHT,
|
||||||
|
justify=justify,
|
||||||
|
indent=indent,
|
||||||
|
line_spacing=None, # Already handled
|
||||||
|
alignment=alignment,
|
||||||
|
pango_width=pango_width
|
||||||
|
)
|
||||||
|
|
||||||
|
# Read the contents
|
||||||
|
tmp.seek(0)
|
||||||
|
return tmp.read()
|
||||||
|
|
||||||
|
|
||||||
class MarkupText(StringMobject):
|
class MarkupText(StringMobject):
|
||||||
# See https://docs.gtk.org/Pango/pango_markup.html
|
# See https://docs.gtk.org/Pango/pango_markup.html
|
||||||
MARKUP_TAGS = {
|
MARKUP_TAGS = {
|
||||||
|
@ -172,59 +223,13 @@ class MarkupText(StringMobject):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_svg_string_by_content(self, content: str) -> str:
|
def get_svg_string_by_content(self, content: str) -> str:
|
||||||
key = hash_string(str((
|
self.content = content
|
||||||
|
return markup_to_svg_string(
|
||||||
content,
|
content,
|
||||||
self.justify,
|
justify=self.justify,
|
||||||
self.indent,
|
indent=self.indent,
|
||||||
self.alignment,
|
alignment=self.alignment,
|
||||||
self.line_width
|
line_width=self.line_width
|
||||||
)))
|
|
||||||
return get_cached_value(key, lambda: self.markup_to_svg_string(content))
|
|
||||||
|
|
||||||
def markup_to_svg_string(self, markup_str: str) -> str:
|
|
||||||
self.validate_markup_string(markup_str)
|
|
||||||
|
|
||||||
# `manimpango` is under construction,
|
|
||||||
# so the following code is intended to suit its interface
|
|
||||||
alignment = _Alignment(self.alignment)
|
|
||||||
if self.line_width is None:
|
|
||||||
pango_width = -1
|
|
||||||
else:
|
|
||||||
pango_width = self.line_width / FRAME_WIDTH * DEFAULT_PIXEL_WIDTH
|
|
||||||
|
|
||||||
with tempfile.NamedTemporaryFile(suffix='.svg', mode='r+') as tmp:
|
|
||||||
manimpango.MarkupUtils.text2svg(
|
|
||||||
text=markup_str,
|
|
||||||
font="", # Already handled
|
|
||||||
slant="NORMAL", # Already handled
|
|
||||||
weight="NORMAL", # Already handled
|
|
||||||
size=1, # Already handled
|
|
||||||
_=0, # Empty parameter
|
|
||||||
disable_liga=False,
|
|
||||||
file_name=tmp.name,
|
|
||||||
START_X=0,
|
|
||||||
START_Y=0,
|
|
||||||
width=DEFAULT_CANVAS_WIDTH,
|
|
||||||
height=DEFAULT_CANVAS_HEIGHT,
|
|
||||||
justify=self.justify,
|
|
||||||
indent=self.indent,
|
|
||||||
line_spacing=None, # Already handled
|
|
||||||
alignment=alignment,
|
|
||||||
pango_width=pango_width
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read the contents
|
|
||||||
tmp.seek(0)
|
|
||||||
return tmp.read()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_markup_string(markup_str: str) -> None:
|
|
||||||
validate_error = manimpango.MarkupUtils.validate(markup_str)
|
|
||||||
if not validate_error:
|
|
||||||
return
|
|
||||||
raise ValueError(
|
|
||||||
f"Invalid markup string \"{markup_str}\"\n" + \
|
|
||||||
f"{validate_error}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Toolkits
|
# Toolkits
|
||||||
|
|
|
@ -1,22 +1,38 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
from manimlib.utils.directories import get_cache_dir
|
from manimlib.utils.directories import get_cache_dir
|
||||||
|
from manimlib.utils.simple_functions import hash_string
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
CACHE_SIZE = 1e9 # 1 Gig
|
CACHE_SIZE = 1e9 # 1 Gig
|
||||||
|
_cache = Cache(get_cache_dir(), size_limit=CACHE_SIZE)
|
||||||
|
|
||||||
|
|
||||||
def get_cached_value(key, value_func, message=""):
|
def cache_on_disk(func: Callable[..., T]) -> Callable[..., T]:
|
||||||
cache = Cache(get_cache_dir(), size_limit=CACHE_SIZE)
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
key = hash_string("".join(map(str, [func.__name__, args, kwargs])))
|
||||||
|
value = _cache.get(key)
|
||||||
|
if value is None:
|
||||||
|
# print(f"Executing {func.__name__}({args[0]}, ...)")
|
||||||
|
value = func(*args, **kwargs)
|
||||||
|
_cache.set(key, value)
|
||||||
|
return value
|
||||||
|
return wrapper
|
||||||
|
|
||||||
value = cache.get(key)
|
|
||||||
if value is None:
|
def clear_cache():
|
||||||
with display_during_execution(message):
|
_cache.clear()
|
||||||
value = value_func()
|
|
||||||
cache.set(key, value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|
|
@ -11,7 +11,9 @@ CUSTOMIZATION = {}
|
||||||
|
|
||||||
def get_customization():
|
def get_customization():
|
||||||
if not CUSTOMIZATION:
|
if not CUSTOMIZATION:
|
||||||
|
print(CUSTOMIZATION)
|
||||||
CUSTOMIZATION.update(get_custom_config())
|
CUSTOMIZATION.update(get_custom_config())
|
||||||
|
print(CUSTOMIZATION)
|
||||||
directories = CUSTOMIZATION["directories"]
|
directories = CUSTOMIZATION["directories"]
|
||||||
# Unless user has specified otherwise, use the system default temp
|
# Unless user has specified otherwise, use the system default temp
|
||||||
# directory for storing tex files, mobject_data, etc.
|
# directory for storing tex files, mobject_data, etc.
|
||||||
|
|
|
@ -8,6 +8,7 @@ import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
from manimlib.utils.cache import cache_on_disk
|
||||||
from manimlib.config import get_custom_config
|
from manimlib.config import get_custom_config
|
||||||
from manimlib.config import get_manim_dir
|
from manimlib.config import get_manim_dir
|
||||||
from manimlib.logger import log
|
from manimlib.logger import log
|
||||||
|
@ -62,10 +63,13 @@ def get_full_tex(content: str, preamble: str = ""):
|
||||||
)) + "\n"
|
)) + "\n"
|
||||||
|
|
||||||
|
|
||||||
|
@cache_on_disk
|
||||||
def latex_to_svg(
|
def latex_to_svg(
|
||||||
latex: str,
|
latex: str,
|
||||||
template: str = "",
|
template: str = "",
|
||||||
additional_preamble: str = ""
|
additional_preamble: str = "",
|
||||||
|
short_tex: str = "",
|
||||||
|
show_message_during_execution: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Convert LaTeX string to SVG string.
|
"""Convert LaTeX string to SVG string.
|
||||||
|
|
||||||
|
@ -81,6 +85,13 @@ def latex_to_svg(
|
||||||
LatexError: If LaTeX compilation fails
|
LatexError: If LaTeX compilation fails
|
||||||
NotImplementedError: If compiler is not supported
|
NotImplementedError: If compiler is not supported
|
||||||
"""
|
"""
|
||||||
|
if show_message_during_execution:
|
||||||
|
max_message_len = 80
|
||||||
|
message = f"Writing {short_tex or latex}"
|
||||||
|
if len(message) > max_message_len:
|
||||||
|
message = message[:max_message_len - 3] + "..."
|
||||||
|
print(message, end="\r")
|
||||||
|
|
||||||
tex_config = get_tex_config()
|
tex_config = get_tex_config()
|
||||||
if template and template != tex_config["template"]:
|
if template and template != tex_config["template"]:
|
||||||
tex_config = get_tex_template_config(template)
|
tex_config = get_tex_template_config(template)
|
||||||
|
@ -147,7 +158,12 @@ def latex_to_svg(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return SVG string
|
# Return SVG string
|
||||||
return process.stdout.decode('utf-8')
|
result = process.stdout.decode('utf-8')
|
||||||
|
|
||||||
|
if show_message_during_execution:
|
||||||
|
print(" " * len(message), end="\r")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class LatexError(Exception):
|
class LatexError(Exception):
|
||||||
|
|
Loading…
Add table
Reference in a new issue