Skip to content

Commit 648855d

Browse files
authored
Merge pull request #1772 from YishiMichael/master
Construct LabelledString base class for MarkupText and MTex
2 parents 39673a8 + 974d9d5 commit 648855d

File tree

8 files changed

+1278
-971
lines changed

8 files changed

+1278
-971
lines changed

manimlib/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from manimlib.mobject.shape_matchers import *
3838
from manimlib.mobject.svg.brace import *
3939
from manimlib.mobject.svg.drawings import *
40+
from manimlib.mobject.svg.labelled_string import *
4041
from manimlib.mobject.svg.mtex_mobject import *
4142
from manimlib.mobject.svg.svg_mobject import *
4243
from manimlib.mobject.svg.tex_mobject import *

manimlib/animation/creation.py

+11-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from manimlib.animation.animation import Animation
99
from manimlib.animation.composition import Succession
10+
from manimlib.mobject.svg.labelled_string import LabelledString
1011
from manimlib.mobject.types.vectorized_mobject import VMobject
1112
from manimlib.utils.bezier import integer_interpolate
1213
from manimlib.utils.config_ops import digest_config
@@ -202,23 +203,19 @@ def update_submobject_list(self, index: int) -> None:
202203
self.mobject.set_submobjects([self.all_submobs[index - 1]])
203204

204205

205-
# TODO, this is broken...
206-
class AddTextWordByWord(Succession):
206+
class AddTextWordByWord(ShowIncreasingSubsets):
207207
CONFIG = {
208208
# If given a value for run_time, it will
209-
# override the time_per_char
209+
# override the time_per_word
210210
"run_time": None,
211-
"time_per_char": 0.06,
211+
"time_per_word": 0.2,
212+
"rate_func": linear,
212213
}
213214

214-
def __init__(self, text_mobject, **kwargs):
215+
def __init__(self, string_mobject, **kwargs):
216+
assert isinstance(string_mobject, LabelledString)
217+
grouped_mobject = string_mobject.get_submob_groups()
215218
digest_config(self, kwargs)
216-
tpc = self.time_per_char
217-
anims = it.chain(*[
218-
[
219-
ShowIncreasingSubsets(word, run_time=tpc * len(word)),
220-
Animation(word, run_time=0.005 * len(word)**1.5),
221-
]
222-
for word in text_mobject
223-
])
224-
super().__init__(*anims, **kwargs)
219+
if self.run_time is None:
220+
self.run_time = self.time_per_word * len(grouped_mobject)
221+
super().__init__(grouped_mobject, **kwargs)

manimlib/animation/transform_matching_parts.py

+97-79
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from manimlib.animation.transform import Transform
1313
from manimlib.mobject.mobject import Mobject
1414
from manimlib.mobject.mobject import Group
15-
from manimlib.mobject.svg.mtex_mobject import MTex
15+
from manimlib.mobject.svg.labelled_string import LabelledString
1616
from manimlib.mobject.types.vectorized_mobject import VGroup
1717
from manimlib.mobject.types.vectorized_mobject import VMobject
1818
from manimlib.utils.config_ops import digest_config
@@ -153,51 +153,66 @@ def get_mobject_key(mobject: Tex) -> str:
153153
return mobject.get_tex()
154154

155155

156-
class TransformMatchingMTex(AnimationGroup):
156+
class TransformMatchingStrings(AnimationGroup):
157157
CONFIG = {
158158
"key_map": dict(),
159+
"transform_mismatches": False,
159160
}
160161

161-
def __init__(self, source_mobject: MTex, target_mobject: MTex, **kwargs):
162+
def __init__(self,
163+
source_mobject: LabelledString,
164+
target_mobject: LabelledString,
165+
**kwargs
166+
):
162167
digest_config(self, kwargs)
163-
assert isinstance(source_mobject, MTex)
164-
assert isinstance(target_mobject, MTex)
168+
assert isinstance(source_mobject, LabelledString)
169+
assert isinstance(target_mobject, LabelledString)
165170
anims = []
166-
rest_source_submobs = source_mobject.submobjects.copy()
167-
rest_target_submobs = target_mobject.submobjects.copy()
168-
169-
def add_anim_from(anim_class, func, source_attr, target_attr=None):
170-
if target_attr is None:
171-
target_attr = source_attr
172-
source_parts = func(source_mobject, source_attr)
173-
target_parts = func(target_mobject, target_attr)
174-
filtered_source_parts = [
175-
submob_part for submob_part in source_parts
176-
if all([
177-
submob in rest_source_submobs
178-
for submob in submob_part
179-
])
180-
]
181-
filtered_target_parts = [
182-
submob_part for submob_part in target_parts
183-
if all([
184-
submob in rest_target_submobs
185-
for submob in submob_part
186-
])
187-
]
188-
if not (filtered_source_parts and filtered_target_parts):
189-
return
190-
anims.append(anim_class(
191-
VGroup(*filtered_source_parts),
192-
VGroup(*filtered_target_parts),
193-
**kwargs
194-
))
195-
for submob in it.chain(*filtered_source_parts):
196-
rest_source_submobs.remove(submob)
197-
for submob in it.chain(*filtered_target_parts):
198-
rest_target_submobs.remove(submob)
199-
200-
def get_submobs_from_keys(mobject, keys):
171+
rest_source_indices = list(range(len(source_mobject.submobjects)))
172+
rest_target_indices = list(range(len(target_mobject.submobjects)))
173+
174+
def add_anims_from(anim_class, func, source_args, target_args=None):
175+
if target_args is None:
176+
target_args = source_args.copy()
177+
for source_arg, target_arg in zip(source_args, target_args):
178+
source_parts = func(source_mobject, source_arg)
179+
target_parts = func(target_mobject, target_arg)
180+
source_indices_lists = source_mobject.indices_lists_of_parts(
181+
source_parts
182+
)
183+
target_indices_lists = target_mobject.indices_lists_of_parts(
184+
target_parts
185+
)
186+
filtered_source_indices_lists = list(filter(
187+
lambda indices_list: all([
188+
index in rest_source_indices
189+
for index in indices_list
190+
]), source_indices_lists
191+
))
192+
filtered_target_indices_lists = list(filter(
193+
lambda indices_list: all([
194+
index in rest_target_indices
195+
for index in indices_list
196+
]), target_indices_lists
197+
))
198+
if not all([
199+
filtered_source_indices_lists,
200+
filtered_target_indices_lists
201+
]):
202+
continue
203+
anims.append(anim_class(source_parts, target_parts, **kwargs))
204+
for index in it.chain(*filtered_source_indices_lists):
205+
rest_source_indices.remove(index)
206+
for index in it.chain(*filtered_target_indices_lists):
207+
rest_target_indices.remove(index)
208+
209+
def get_common_substrs(func):
210+
return sorted([
211+
substr for substr in func(source_mobject)
212+
if substr and substr in func(target_mobject)
213+
], key=len, reverse=True)
214+
215+
def get_parts_from_keys(mobject, keys):
201216
if not isinstance(keys, tuple):
202217
keys = (keys,)
203218
indices = []
@@ -207,52 +222,55 @@ def get_submobs_from_keys(mobject, keys):
207222
elif isinstance(key, range):
208223
indices.extend(key)
209224
elif isinstance(key, str):
210-
all_parts = mobject.get_parts_by_tex(key)
225+
all_parts = mobject.get_parts_by_string(key)
211226
indices.extend(it.chain(*[
212227
mobject.indices_of_part(part) for part in all_parts
213228
]))
214229
else:
215230
raise TypeError(key)
216231
return VGroup(VGroup(*[
217-
mobject[i] for i in remove_list_redundancies(indices)
232+
mobject[index] for index in remove_list_redundancies(indices)
218233
]))
219234

220-
for source_key, target_key in self.key_map.items():
221-
add_anim_from(
222-
ReplacementTransform, get_submobs_from_keys,
223-
source_key, target_key
224-
)
225-
226-
common_specified_substrings = sorted(list(
227-
set(source_mobject.get_specified_substrings()).intersection(
228-
target_mobject.get_specified_substrings()
229-
)
230-
), key=len, reverse=True)
231-
for part_tex_string in common_specified_substrings:
232-
add_anim_from(
233-
FadeTransformPieces, MTex.get_parts_by_tex, part_tex_string
234-
)
235-
236-
common_submob_tex_strings = {
237-
source_submob.get_tex() for source_submob in source_mobject
238-
}.intersection({
239-
target_submob.get_tex() for target_submob in target_mobject
240-
})
241-
for tex_string in common_submob_tex_strings:
242-
add_anim_from(
243-
FadeTransformPieces,
244-
lambda mobject, attr: VGroup(*[
245-
VGroup(mob) for mob in mobject
246-
if mob.get_tex() == attr
247-
]),
248-
tex_string
249-
)
250-
251-
anims.append(FadeOutToPoint(
252-
VGroup(*rest_source_submobs), target_mobject.get_center(), **kwargs
253-
))
254-
anims.append(FadeInFromPoint(
255-
VGroup(*rest_target_submobs), source_mobject.get_center(), **kwargs
256-
))
235+
add_anims_from(
236+
ReplacementTransform, get_parts_from_keys,
237+
self.key_map.keys(), self.key_map.values()
238+
)
239+
add_anims_from(
240+
FadeTransformPieces,
241+
LabelledString.get_parts_by_string,
242+
get_common_substrs(LabelledString.get_specified_substrs)
243+
)
244+
add_anims_from(
245+
FadeTransformPieces,
246+
LabelledString.get_parts_by_group_substr,
247+
get_common_substrs(LabelledString.get_group_substrs)
248+
)
249+
250+
fade_source = VGroup(*[
251+
source_mobject[index]
252+
for index in rest_source_indices
253+
])
254+
fade_target = VGroup(*[
255+
target_mobject[index]
256+
for index in rest_target_indices
257+
])
258+
if self.transform_mismatches:
259+
anims.append(ReplacementTransform(
260+
fade_source,
261+
fade_target,
262+
**kwargs
263+
))
264+
else:
265+
anims.append(FadeOutToPoint(
266+
fade_source,
267+
target_mobject.get_center(),
268+
**kwargs
269+
))
270+
anims.append(FadeInFromPoint(
271+
fade_target,
272+
source_mobject.get_center(),
273+
**kwargs
274+
))
257275

258276
super().__init__(*anims)

0 commit comments

Comments
 (0)