Merge pull request #1952 from 3b1b/video-work

More miscellaneous tweaks and fixes
This commit is contained in:
Grant Sanderson 2022-12-29 15:53:02 -08:00 committed by GitHub
commit 124c83d94e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 291 additions and 302 deletions

View file

@ -29,6 +29,7 @@ class AnimationGroup(Animation):
run_time: float = -1, # If negative, default to sum of inputed animation runtimes
lag_ratio: float = 0.0,
group: Mobject | None = None,
group_type: type = Group,
**kwargs
):
self.animations = [prepare_animation(anim) for anim in animations]
@ -38,7 +39,7 @@ class AnimationGroup(Animation):
self.lag_ratio = lag_ratio
self.group = group
if self.group is None:
self.group = Group(*remove_list_redundancies(
self.group = group_type(*remove_list_redundancies(
[anim.mobject for anim in self.animations]
))
@ -49,7 +50,7 @@ class AnimationGroup(Animation):
**kwargs
)
def get_all_mobjects(self) -> Group:
def get_all_mobjects(self) -> Mobject:
return self.group
def begin(self) -> None:

View file

@ -4,26 +4,20 @@ import itertools as it
import numpy as np
from manimlib.animation.animation import Animation
from manimlib.animation.composition import AnimationGroup
from manimlib.animation.fading import FadeInFromPoint
from manimlib.animation.fading import FadeOutToPoint
from manimlib.animation.fading import FadeTransformPieces
from manimlib.animation.fading import FadeTransform
from manimlib.animation.transform import ReplacementTransform
from manimlib.animation.transform import Transform
from manimlib.mobject.mobject import Mobject
from manimlib.mobject.mobject import Group
from manimlib.mobject.svg.string_mobject import StringMobject
from manimlib.mobject.svg.old_tex_mobject import OldTex
from manimlib.mobject.svg.tex_mobject import Tex
from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from manimlib.mobject.svg.old_tex_mobject import SingleStringTex
from manimlib.scene.scene import Scene
@ -151,13 +145,14 @@ class TransformMatchingStrings(AnimationGroup):
match_animation: type = Transform,
mismatch_animation: type = Transform,
run_time=2,
lag_ratio=0,
**kwargs,
):
self.source = source
self.target = target
matched_keys = matched_keys or list()
key_map = key_map or dict()
self.anim_config = dict(run_time=run_time, **kwargs)
self.anim_config = dict(**kwargs)
# We will progressively build up a list of transforms
# from characters in source to those in target. These
@ -208,7 +203,12 @@ class TransformMatchingStrings(AnimationGroup):
target_char, source.get_center(),
**self.anim_config
))
super().__init__(*self.anims)
super().__init__(
*self.anims,
run_time=run_time,
lag_ratio=lag_ratio,
group_type=VGroup,
)
def add_transform(
self,
@ -230,18 +230,10 @@ class TransformMatchingStrings(AnimationGroup):
self.target_chars.remove(char)
def find_pairs_with_matching_shapes(self, chars1, chars2) -> list[tuple[VMobject, VMobject]]:
for char in (*chars1, *chars2):
char.save_state()
char.set_height(1)
char.center()
result = []
for char1, char2 in it.product(chars1, chars2):
p1 = char1.get_points()
p2 = char2.get_points()
if len(p1) == len(p2) and np.isclose(p1, p2 , atol=1e-1).all():
if char1.has_same_shape_as(char2):
result.append((char1, char2))
for char in (*chars1, *chars2):
char.restore()
return result
def clean_up_from_scene(self, scene: Scene) -> None:

View file

@ -491,4 +491,6 @@ def get_configuration(args: Namespace) -> dict:
"presenter_mode": args.presenter_mode,
"leave_progress_bars": args.leave_progress_bars,
"show_animation_progress": args.show_animation_progress,
"embed_exception_mode": custom_config["embed_exception_mode"],
"embed_error_sound": custom_config["embed_error_sound"],
}

View file

@ -42,3 +42,5 @@ camera_resolutions:
4k: "3840x2160"
default_resolution: "high"
fps: 30
embed_exception_mode: "Verbose"
embed_error_sound: False

View file

@ -54,22 +54,11 @@ def prompt_user_for_choice(scene_classes):
def get_scene_config(config):
return dict([
(key, config[key])
for key in [
"window_config",
"camera_config",
"file_writer_config",
"skip_animations",
"start_at_animation_number",
"end_at_animation_number",
"leave_progress_bars",
"show_animation_progress",
"preview",
"presenter_mode",
]
])
scene_parameters = inspect.signature(Scene).parameters.keys()
return {
key: config[key]
for key in set(scene_parameters).intersection(config.keys())
}
def compute_total_frames(scene_class, scene_config):
"""

View file

@ -657,7 +657,7 @@ class Mobject(object):
self.match_updaters(mobject)
return self
def looks_identical(self, mobject: Mobject):
def looks_identical(self, mobject: Mobject) -> bool:
fam1 = self.family_members_with_points()
fam2 = mobject.family_members_with_points()
if len(fam1) != len(fam2):
@ -667,15 +667,20 @@ class Mobject(object):
if set(d1).difference(d2):
return False
for key in d1:
eq = (d1[key] == d2[key])
if isinstance(eq, bool):
if not eq:
return False
else:
if not eq.all():
return False
if not np.isclose(d1[key], d2[key]).all():
return False
return True
def has_same_shape_as(self, mobject: Mobject) -> bool:
# Normalize both point sets by centering and making height 1
points1, points2 = (
(m.get_all_points() - m.get_center()) / m.get_height()
for m in (self, mobject)
)
if len(points1) != len(points2):
return False
return bool(np.isclose(points1, points2).all())
# Creating new Mobjects from this one
def replicate(self, n: int) -> Group:
@ -1723,7 +1728,7 @@ class Mobject(object):
for sm, sm1, sm2 in zip(self.get_family(), mobject1.get_family(), mobject2.get_family()):
keys = sm.data.keys() & sm1.data.keys() & sm2.data.keys()
sm.lock_data(list(filter(
lambda key: np.all(sm1.data[key] == sm2.data[key]),
lambda key: (sm1.data[key] == sm2.data[key]).all(),
keys,
)))
return self

View file

@ -547,7 +547,7 @@ class StringMobject(SVGMobject, ABC):
def select_parts(self, selector: Selector) -> VGroup:
specified_substrings = self.get_specified_substrings()
if isinstance(selector, str) and selector not in specified_substrings:
if isinstance(selector, (str, re.Pattern)) and selector not in specified_substrings:
return self.select_unisolated_substring(selector)
indices_list = self.get_submob_indices_lists_by_selector(selector)
return self.build_parts_from_indices_lists(indices_list)
@ -563,11 +563,14 @@ class StringMobject(SVGMobject, ABC):
def substr_to_path_count(self, substr: str) -> int:
return len(re.sub(R"\s", "", substr))
def select_unisolated_substring(self, substr: str) -> VGroup:
def select_unisolated_substring(self, pattern: str | re.Pattern) -> VGroup:
if isinstance(pattern, str):
pattern = re.compile(re.escape(pattern))
result = []
for match in re.finditer(re.escape(substr), self.string):
for match in re.finditer(pattern, self.string):
index = match.start()
start = self.substr_to_path_count(self.string[:index])
substr = match.group()
end = start + self.substr_to_path_count(substr)
result.append(self[start:end])
return VGroup(*result)

View file

@ -292,15 +292,13 @@ class VMobjectFromSVGPath(VMobject):
def __init__(
self,
path_obj: se.Path,
long_lines: bool = False,
should_subdivide_sharp_curves: bool = False,
should_remove_null_curves: bool = False,
should_remove_null_curves: bool = True,
**kwargs
):
# Get rid of arcs
path_obj.approximate_arcs_with_quads()
self.path_obj = path_obj
self.long_lines = long_lines
self.should_subdivide_sharp_curves = should_subdivide_sharp_curves
self.should_remove_null_curves = should_remove_null_curves
super().__init__(**kwargs)

View file

@ -148,11 +148,6 @@ class VMobject(Mobject):
raise Exception("All submobjects must be of type VMobject")
super().add(*vmobjects)
def copy(self, deep: bool = False) -> VMobject:
result = super().copy(deep)
result.shader_wrapper_list = [sw.copy() for sw in self.shader_wrapper_list]
return result
# Colors
def init_colors(self):
self.set_fill(
@ -809,6 +804,11 @@ class VMobject(Mobject):
# Alignment
def align_points(self, vmobject: VMobject):
if self.get_num_points() == len(vmobject.get_points()):
# If both have fill, and they have the same shape, just
# give them the same triangulation so that it's not recalculated
# needlessly throughout an animation
if self.has_fill() and vmobject.has_fill() and self.has_same_shape_as(vmobject):
vmobject.triangulation = self.triangulation
return
for mob in self, vmobject:
@ -1077,14 +1077,6 @@ class VMobject(Mobject):
render_primitive=self.render_primitive,
)
self.shader_wrapper_list = [
self.stroke_shader_wrapper.copy(), # Use for back stroke
self.fill_shader_wrapper.copy(),
self.stroke_shader_wrapper.copy(),
]
for sw in self.shader_wrapper_list:
sw.uniforms = self.uniforms
def refresh_shader_wrapper_id(self):
for wrapper in [self.fill_shader_wrapper, self.stroke_shader_wrapper]:
wrapper.refresh_id()
@ -1107,30 +1099,29 @@ class VMobject(Mobject):
# Build up data lists
fill_shader_wrappers = []
stroke_shader_wrappers = []
back_stroke_shader_wrappers = []
for submob in self.family_members_with_points():
if submob.has_fill():
fill_shader_wrappers.append(submob.get_fill_shader_wrapper())
if submob.has_stroke():
ssw = submob.get_stroke_shader_wrapper()
if submob.draw_stroke_behind_fill:
back_stroke_shader_wrappers.append(ssw)
else:
stroke_shader_wrappers.append(ssw)
stroke_shader_wrappers.append(submob.get_stroke_shader_wrapper())
if submob.draw_stroke_behind_fill:
self.draw_stroke_behind_fill = True
# Combine data lists
sw_lists = [
back_stroke_shader_wrappers,
fill_shader_wrappers,
stroke_shader_wrappers,
]
for sw, sw_list in zip(self.shader_wrapper_list, sw_lists):
self_sws = [self.fill_shader_wrapper, self.stroke_shader_wrapper]
sw_lists = [fill_shader_wrappers, stroke_shader_wrappers]
for sw, sw_list in zip(self_sws, sw_lists):
if not sw_list:
sw.vert_data = resize_array(sw.vert_data, 0)
continue
sw.read_in(*sw_list)
if sw is sw_list[0]:
sw.combine_with(*sw_list[1:])
else:
sw.read_in(*sw_list)
sw.depth_test = any(sw.depth_test for sw in sw_list)
sw.uniforms.update(sw_list[0].uniforms)
return list(filter(lambda sw: len(sw.vert_data) > 0, self.shader_wrapper_list))
if self.draw_stroke_behind_fill:
self_sws.reverse()
return [sw for sw in self_sws if len(sw.vert_data) > 0]
def get_stroke_shader_data(self) -> np.ndarray:
points = self.get_points()

View file

@ -16,15 +16,18 @@ import numpy as np
from tqdm import tqdm as ProgressDisplay
from manimlib.animation.animation import prepare_animation
from manimlib.animation.fading import VFadeInThenOut
from manimlib.camera.camera import Camera
from manimlib.config import get_module
from manimlib.constants import ARROW_SYMBOLS
from manimlib.constants import DEFAULT_WAIT_TIME
from manimlib.constants import COMMAND_MODIFIER
from manimlib.constants import SHIFT_MODIFIER
from manimlib.constants import RED
from manimlib.event_handler import EVENT_DISPATCHER
from manimlib.event_handler.event_type import EventType
from manimlib.logger import log
from manimlib.mobject.frame import FullScreenRectangle
from manimlib.mobject.mobject import _AnimationBuilder
from manimlib.mobject.mobject import Group
from manimlib.mobject.mobject import Mobject
@ -34,7 +37,6 @@ from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.scene.scene_file_writer import SceneFileWriter
from manimlib.utils.family_ops import extract_mobject_family_members
from manimlib.utils.family_ops import recursive_mobject_remove
from manimlib.utils.iterables import list_difference_update
from typing import TYPE_CHECKING
@ -74,6 +76,8 @@ class Scene(object):
preview: bool = True,
presenter_mode: bool = False,
show_animation_progress: bool = False,
embed_exception_mode: str = "",
embed_error_sound: bool = False,
):
self.skip_animations = skip_animations
self.always_update_mobjects = always_update_mobjects
@ -83,6 +87,8 @@ class Scene(object):
self.preview = preview
self.presenter_mode = presenter_mode
self.show_animation_progress = show_animation_progress
self.embed_exception_mode = embed_exception_mode
self.embed_error_sound = embed_error_sound
self.camera_config = {**self.default_camera_config, **camera_config}
self.window_config = {**self.default_window_config, **window_config}
@ -249,6 +255,22 @@ class Scene(object):
shell.events.register("post_run_cell", post_cell_func)
# Flash border, and potentially play sound, on exceptions
def custom_exc(shell, etype, evalue, tb, tb_offset=None):
# still show the error don't just swallow it
shell.showtraceback((etype, evalue, tb), tb_offset=tb_offset)
if self.embed_error_sound:
os.system("printf '\a'")
self.play(VFadeInThenOut(
FullScreenRectangle().set_stroke(RED, 30).set_fill(opacity=0),
run_time=0.5,
))
shell.set_custom_exc((Exception,), custom_exc)
# Set desired exception mode
shell.magic(f"xmode {self.embed_exception_mode}")
# Launch shell
shell(
local_ns=local_ns,

View file

@ -131,7 +131,8 @@ class ShaderWrapper(object):
self.refresh_id()
def combine_with(self, *shader_wrappers: ShaderWrapper) -> ShaderWrapper:
self.read_in(self.copy(), *shader_wrappers)
if len(shader_wrappers) > 0:
self.read_in(self.copy(), *shader_wrappers)
return self
def read_in(self, *shader_wrappers: ShaderWrapper) -> ShaderWrapper:

View file

@ -1,57 +1,41 @@
from __future__ import annotations
import re
from functools import lru_cache
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import List, Tuple
@lru_cache(maxsize=1)
def get_pattern_symbol_count_pairs() -> List[Tuple[str, int]]:
from manimlib.utils.tex_to_symbol_count import TEX_TO_SYMBOL_COUNT
# Gather all keys of previous map, grouped by common value
count_to_tex_list = dict()
for command, num in TEX_TO_SYMBOL_COUNT.items():
if num not in count_to_tex_list:
count_to_tex_list[num] = []
count_to_tex_list[num].append(command)
# Create a list associating each count with a regular expression
# that will find any tex commands matching that list
pattern_symbol_count_pairs = list()
# Account for patterns like \begin{align} and \phantom{thing}
# which, together with the bracketed content account for zero paths.
# Deliberately put this first in the list
tex_list = ["begin", "end", "phantom"]
pattern_symbol_count_pairs.append(
("|".join(r"\\" + s + r"\{[^\\}]+\}" for s in tex_list), 0)
)
for count, tex_list in count_to_tex_list.items():
pattern = "|".join(r"\\" + s for s in tex_list)
pattern_symbol_count_pairs.append((pattern, count))
# Assume all other expressions of the form \thing are drawn with one path
# Deliberately put this last in the list
pattern_symbol_count_pairs.append((r"\\[a-zA-Z]+", 1))
return pattern_symbol_count_pairs
from manimlib.utils.tex_to_symbol_count import TEX_TO_SYMBOL_COUNT
def num_tex_symbols(tex: str) -> int:
"""
This function attempts to estimate the number of symbols that
a given string of tex would produce.
Warning, it may not behave perfectly
"""
# First, remove patterns like \begin{align}, \phantom{thing},
# \begin{array}{cc}, etc.
pattern = "|".join(
r"(\\" + s + ")" + r"(\{\w+\})?(\{\w+\})?(\[\w+\])?"
for s in ["begin", "end", "phantom"]
)
for tup in re.findall(pattern, tex):
tex = tex.replace("".join(tup), " ")
# Progressively count the symbols associated with certain tex commands,
# and remove those commands from the string, adding the number of symbols
# that command creates
total = 0
for pattern, count in get_pattern_symbol_count_pairs():
total += count * len(re.findall(pattern, tex))
tex = re.sub(pattern, " ", tex) # Remove that pattern
# Start with the special case \sqrt[number]
for substr in re.findall(r"\\sqrt\[[0-9]+\]", tex):
total += len(substr) - 5 # e.g. \sqrt[3] is 3 symbols
tex = tex.replace(substr, " ")
general_command = r"\\[a-zA-Z!,-/:;<>]+"
for substr in re.findall(general_command, tex):
total += TEX_TO_SYMBOL_COUNT.get(substr, 1)
tex = tex.replace(substr, " ")
# Count remaining characters
total += sum(map(lambda c: c not in "^{} \n\t_$", tex))
total += sum(map(lambda c: c not in "^{} \n\t_$\\&", tex))
return total

View file

@ -1,182 +1,181 @@
TEX_TO_SYMBOL_COUNT = {
"!": 0,
",": 0,
",": 0,
"-": 0,
"-": 0,
"/": 0,
":": 0,
";": 0,
";": 0,
">": 0,
"aa": 0,
"AA": 0,
"ae": 0,
"AE": 0,
"arccos": 6,
"arcsin": 6,
"arctan": 6,
"arg": 3,
"author": 0,
"bf": 0,
"bibliography": 0,
"bibliographystyle": 0,
"big": 0,
"Big": 0,
"bigodot": 4,
"bigoplus": 5,
"bigskip": 0,
"bmod": 3,
"boldmath": 0,
"bottomfraction": 2,
"bowtie": 2,
"cal": 0,
"cdots": 3,
"centering": 0,
"cite": 2,
"cong": 2,
"contentsline": 0,
"cos": 3,
"cosh": 4,
"cot": 3,
"coth": 4,
"csc": 3,
"date": 0,
"dblfloatpagefraction": 2,
"dbltopfraction": 2,
"ddots": 3,
"deg": 3,
"det": 3,
"dim": 3,
"displaystyle": 0,
"div": 2,
"doteq": 2,
"dotfill": 0,
"emph": 0,
"exp": 3,
"fbox": 4,
"floatpagefraction": 2,
"flushbottom": 0,
"footnotesize": 0,
"footnotetext": 0,
"frame": 2,
"framebox": 4,
"fussy": 0,
"gcd": 3,
"ghost": 0,
"glossary": 0,
"hfill": 0,
"hom": 3,
"hookleftarrow": 2,
"hookrightarrow": 2,
"hrulefill": 0,
"huge": 0,
"Huge": 0,
"hyphenation": 0,
"iff": 2,
"Im": 2,
"index": 0,
"inf": 3,
"it": 0,
"ker": 3,
"l": 0,
"L": 0,
"label": 0,
"large": 0,
"Large": 0,
"LARGE": 0,
"ldots": 3,
"lefteqn": 0,
"lg": 2,
"lim": 3,
"liminf": 6,
"limsup": 6,
"linebreak": 0,
"ln": 2,
"log": 3,
"longleftarrow": 2,
"Longleftarrow": 2,
"longleftrightarrow": 2,
"Longleftrightarrow": 2,
"longmapsto": 3,
"longrightarrow": 2,
"Longrightarrow": 2,
"makebox": 0,
"mapsto": 2,
"markright": 0,
"max": 3,
"mbox": 0,
"medskip": 0,
"min": 3,
"mit": 0,
"models": 2,
"ne": 2,
"neq": 2,
"newline": 0,
"noindent": 0,
"nolinebreak": 0,
"nonumber": 0,
"nopagebreak": 0,
"normalmarginpar": 0,
"normalsize": 0,
"notin": 2,
"o": 0,
"O": 0,
"obeycr": 0,
"oe": 0,
"OE": 0,
"overbrace": 4,
"pagebreak": 0,
"pagenumbering": 0,
"pageref": 2,
"pmod": 5,
"Pr": 2,
"protect": 0,
"qquad": 0,
"quad": 0,
"raggedbottom": 0,
"raggedleft": 0,
"raggedright": 0,
"Re": 2,
"ref": 2,
"restorecr": 0,
"reversemarginpar": 0,
"rm": 0,
"sc": 0,
"scriptscriptstyle": 0,
"scriptsize": 0,
"scriptstyle": 0,
"sec": 3,
"sf": 0,
"shortstack": 0,
"sin": 3,
"sinh": 4,
"sl": 0,
"sloppy": 0,
"small": 0,
"Small": 0,
"smallskip": 0,
"sqrt": 2,
"ss": 0,
"sup": 3,
"tan": 3,
"tanh": 4,
"textbf": 0,
"textfraction": 2,
"textstyle": 0,
"thicklines": 0,
"thinlines": 0,
"thinspace": 0,
"tiny": 0,
"title": 0,
"today": 15,
"topfraction": 2,
"tt": 0,
"typeout": 0,
"unboldmath": 0,
"underbrace": 6,
"underline": 0,
"value": 0,
"vdots": 3,
"vline": 0
R"\!": 0,
R"\,": 0,
R"\-": 0,
R"\/": 0,
R"\:": 0,
R"\;": 0,
R"\>": 0,
R"\aa": 0,
R"\AA": 0,
R"\ae": 0,
R"\AE": 0,
R"\arccos": 6,
R"\arcsin": 6,
R"\arctan": 6,
R"\arg": 3,
R"\author": 0,
R"\bf": 0,
R"\bibliography": 0,
R"\bibliographystyle": 0,
R"\big": 0,
R"\Big": 0,
R"\bigodot": 4,
R"\bigoplus": 5,
R"\bigskip": 0,
R"\bmod": 3,
R"\boldmath": 0,
R"\bottomfraction": 2,
R"\bowtie": 2,
R"\cal": 0,
R"\cdots": 3,
R"\centering": 0,
R"\cite": 2,
R"\cong": 2,
R"\contentsline": 0,
R"\cos": 3,
R"\cosh": 4,
R"\cot": 3,
R"\coth": 4,
R"\csc": 3,
R"\date": 0,
R"\dblfloatpagefraction": 2,
R"\dbltopfraction": 2,
R"\ddots": 3,
R"\deg": 3,
R"\det": 3,
R"\dim": 3,
R"\displaystyle": 0,
R"\div": 2,
R"\doteq": 2,
R"\dotfill": 0,
R"\emph": 0,
R"\exp": 3,
R"\fbox": 4,
R"\floatpagefraction": 2,
R"\flushbottom": 0,
R"\footnotesize": 0,
R"\footnotetext": 0,
R"\frame": 2,
R"\framebox": 4,
R"\fussy": 0,
R"\gcd": 3,
R"\ghost": 0,
R"\glossary": 0,
R"\hfill": 0,
R"\hom": 3,
R"\hookleftarrow": 2,
R"\hookrightarrow": 2,
R"\hrulefill": 0,
R"\huge": 0,
R"\Huge": 0,
R"\hyphenation": 0,
R"\iff": 2,
R"\Im": 2,
R"\index": 0,
R"\inf": 3,
R"\it": 0,
R"\ker": 3,
R"\l": 0,
R"\L": 0,
R"\label": 0,
R"\large": 0,
R"\Large": 0,
R"\LARGE": 0,
R"\ldots": 3,
R"\lefteqn": 0,
R"\left": 0,
R"\lg": 2,
R"\lim": 3,
R"\liminf": 6,
R"\limsup": 6,
R"\linebreak": 0,
R"\ln": 2,
R"\log": 3,
R"\longleftarrow": 2,
R"\Longleftarrow": 2,
R"\longleftrightarrow": 2,
R"\Longleftrightarrow": 2,
R"\longmapsto": 3,
R"\longrightarrow": 2,
R"\Longrightarrow": 2,
R"\makebox": 0,
R"\mapsto": 2,
R"\markright": 0,
R"\max": 3,
R"\mbox": 0,
R"\medskip": 0,
R"\min": 3,
R"\mit": 0,
R"\models": 2,
R"\ne": 2,
R"\neq": 2,
R"\newline": 0,
R"\noindent": 0,
R"\nolinebreak": 0,
R"\nonumber": 0,
R"\nopagebreak": 0,
R"\normalmarginpar": 0,
R"\normalsize": 0,
R"\notin": 2,
R"\o": 0,
R"\O": 0,
R"\obeycr": 0,
R"\oe": 0,
R"\OE": 0,
R"\overbrace": 4,
R"\pagebreak": 0,
R"\pagenumbering": 0,
R"\pageref": 2,
R"\pmod": 5,
R"\Pr": 2,
R"\protect": 0,
R"\qquad": 0,
R"\quad": 0,
R"\raggedbottom": 0,
R"\raggedleft": 0,
R"\raggedright": 0,
R"\Re": 2,
R"\ref": 2,
R"\restorecr": 0,
R"\reversemarginpar": 0,
R"\right": 0,
R"\rm": 0,
R"\sc": 0,
R"\scriptscriptstyle": 0,
R"\scriptsize": 0,
R"\scriptstyle": 0,
R"\sec": 3,
R"\sf": 0,
R"\shortstack": 0,
R"\sin": 3,
R"\sinh": 4,
R"\sl": 0,
R"\sloppy": 0,
R"\small": 0,
R"\Small": 0,
R"\smallskip": 0,
R"\sqrt": 2,
R"\ss": 0,
R"\sup": 3,
R"\tan": 3,
R"\tanh": 4,
R"\textbf": 0,
R"\textfraction": 2,
R"\textstyle": 0,
R"\thicklines": 0,
R"\thinlines": 0,
R"\thinspace": 0,
R"\tiny": 0,
R"\title": 0,
R"\today": 15,
R"\topfraction": 2,
R"\tt": 0,
R"\typeout": 0,
R"\unboldmath": 0,
R"\underbrace": 6,
R"\underline": 0,
R"\value": 0,
R"\vdots": 3,
R"\vline": 0
}