Last active
April 30, 2024 12:07
-
-
Save abul4fia/e3547d788a0ad632a962c49928844edf to your computer and use it in GitHub Desktop.
Generic formula transformation under control
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This is an example which uses the "low-level" interface, specifying | |
the indexes or slices() that should be transformed one into | |
another. | |
""" | |
class Test(Scene): | |
def construct(self): | |
m1 = MathTex(r"\frac{x}{y} = \frac{a}{b}\times5") | |
self.add(m1) | |
m2 = MathTex(r"x\cdot b = y\cdot a\times5") | |
# Transform element 2 into 4, 6 into 2, 4 into 6 | |
self.play(TransformMatchingSlices(m1, m2, | |
slice_map=[(2,4), (6,2), (4,6)])) | |
# Transform element 0:3 into 0:6, 4:7 into 7:9 | |
m3 = MathTex(r"\text{Foobar} = \text{Mu}") | |
self.play(TransformMatchingSlices(m2, m3, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This is an example of the higher-level interface in which you can use | |
the function `get_slice_map()` to get the map of slices of indexes to pass | |
instead of having to figure out "manually" those indexes | |
""" | |
class Test(Scene): | |
def construct(self): | |
m1 = MathTex(r"\frac{x}{y} = \frac{a}{b}\times5") | |
self.add(m1) | |
m2 = MathTex(r"x\cdot b = y\cdot a\times5") | |
slice_map = get_slice_map(m1, m2, [("y", "y"), ("b", "b"), ("a", "a")]) | |
self.play(TransformMatchingSlices(m1, m2, slice_map=slice_map)) | |
m3 = MathTex(r"\text{Foobar} = \text{Mu} \times 5") | |
slice_map = get_slice_map(m2, m3, [(r"x\cdot b", r"\text{Foobar}"), | |
(r"y\cdot a", r"\text{Mu}")]) | |
self.play(TransformMatchingSlices(m2, m3, slice_map=slice_map)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code requires the functions defined in the gist | |
# https://gist.github.com/abul4fia/3bbe8e0c1d19a007cad035bb8be90387 | |
from manim.animation.transform_matching_parts import TransformMatchingAbstractBase | |
class TransformMatchingSlices(TransformMatchingAbstractBase): | |
def __init__( | |
self, | |
mobject: Mobject, | |
target_mobject: Mobject, | |
transform_mismatches: bool = False, | |
fade_transform_mismatches: bool = False, | |
key_map: dict | None = None, | |
slice_map: list | None = None, | |
**kwargs, | |
): | |
if slice_map is None: | |
slice_map = tuple() | |
self.mobject = mobject | |
self.target_mobject = target_mobject | |
self.mobject_slices = [] | |
self.target_mobject_slices = [] | |
mobject_used = set() | |
target_mobject_used = set() | |
for i, (o, t) in enumerate(slice_map): | |
o_indexes = self.unroll_indexes(o) | |
t_indexes = self.unroll_indexes(t) | |
self.mobject_slices.append(self.get_submobjects_group(self.mobject, o_indexes, i)) | |
self.target_mobject_slices.append(self.get_submobjects_group(self.target_mobject, t_indexes, i)) | |
mobject_used.update(o_indexes) | |
target_mobject_used.update(t_indexes) | |
mobject_unused = set(range(len(self.mobject[0]))) - mobject_used | |
target_mobject_unused = set(range(len(self.target_mobject[0]))) - target_mobject_used | |
self.mobject_slices.append(self.get_submobjects_group(self.mobject, sorted(mobject_unused), len(slice_map))) | |
self.target_mobject_slices.append(self.get_submobjects_group(self.target_mobject, sorted(target_mobject_unused), len(slice_map))) | |
super().__init__( | |
mobject, | |
target_mobject, | |
transform_mismatches=transform_mismatches, | |
fade_transform_mismatches=fade_transform_mismatches, | |
key_map=key_map, | |
**kwargs, | |
) | |
def unroll_indexes(self, i): | |
result = [] | |
if isinstance(i, int): | |
result.append(i) | |
elif isinstance(i, slice): | |
result.extend(list(range(i.start, i.stop))) | |
elif isinstance(i, tuple): | |
result.extend(list(range(*i))) | |
elif isinstance(i, list): | |
for e in i: | |
result.extend(self.unroll_indexes(e)) | |
else: | |
raise ValueError(f"Invalid index type: {type(i)}") | |
return sorted(result) | |
def get_mobject_parts(self, mobject: Mobject) -> list[Mobject]: | |
if mobject == self.mobject: | |
return self.mobject_slices | |
else: | |
return self.target_mobject_slices | |
def get_submobjects_group(self, mobject:Mobject, parts:list, i:int) -> Mobject: | |
g = VGroup(*[mobject[0][part] for part in parts]) | |
g.matching_key = i | |
return g | |
def get_mobject_key(self, mobject: Mobject) -> int: | |
return getattr(mobject, "matching_key", -1) | |
def get_slice_map(mobject: Mobject, target_mobject: Mobject, mapping: list[tuple], str_to_mobject=MathTex): | |
"""Returns the slice map to be used with TransformMatchingSlices | |
The inputs are: | |
mobject: the source object to be transformed (usually a MathTex or Tex object) | |
target_mobject: the target of the transformation | |
mapping: a list of tuples, being each tuple two strings, or two lists of strings | |
For each tuple, the first element is a string to be found in the source mobject | |
and the secon one is a string to be found in the target mobject | |
The search of those strings is made by shape, not by text | |
Returns: | |
a list of tuples corresponding to the slices of the matched shapes in each object | |
""" | |
slice_map = [] | |
for _from, _to in mapping: | |
if isinstance(_from, str): | |
_from = [str_to_mobject(_from)] | |
else: | |
_from = [str_to_mobject(s) for s in _from] | |
if isinstance(_to, str): | |
_to = [str_to_mobject(_to)] | |
else: | |
_to = [str_to_mobject(s) for s in _to] | |
slice_map.append((search_shapes_in_text(mobject, _from), | |
search_shapes_in_text(target_mobject, _to)) | |
) | |
return slice_map | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment