-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code Transform Animation applies to the entire code instead of the changed lines #4103
Comments
I'll create fix PR soon. :) |
I agree, the current code change animations are terrible. I've created a DynamicCode mobject that allows more natural code change animations. Maybe this will be helpful to you. Would be awesome if more natural code diffing animations were the default though. |
Hi! I'm really interested in improving the Code Mobject animation. While looking into it, I realized that the current Code object doesn’t retain text information, so it seems like this would require many internal changes.... This is actually my first contribution, so it feels bit challenging. But I’m excited to keep working on it because I want to create Computer Science videos with Manim in the future. Do you think it would make sense to build this feature on top of your DynamicCode Mobject? |
I'm honestly not sure. There are some things about |
I checked out your work on DynamicCode and ran it. it works really great :) Currently, Manim’s Code object has only SVG, so we should switch it with Text objects. your code do it maybe character level. If you have any additional advice or suggestions, I’d really appreciate it. thanks |
Great to hear. To sum up the issues I ran into in developing DynamicCode, a simple Transform from one Code mobject to another looks terrible because it does not understand that unchanged lines of code should remain in place or at most shift around and does not introduce new glyphs as if they were typed. The issue as far as I understand it is that if the SVG vertices between the previous and updated Code objects are not a 1:1 match, the Transform essentially just recreates the new mobject entirely as it can't figure out what you want it to do. To get around this, DynamicCode first moves all unchanged glyphs around in which case there is a 1:1 match with old and new vertices and so Transform shifts these glyphs around in a smooth fashion. After that the new glyphs are added with opacity=0 instantaneously (no animation) to avoid a horrible animation where they spring up from a point or some such, I don't recall exactly. Thereafter the new glyphs are made visible (opacity=1) one by one to simulate typing. One idea I've had, but haven't looked into to see how feasible it is, is instead of the above hacky process to see if you can specify the transform paths for the glyphs. Might be worth looking into, but not sure it gets you around all of the issues. Another issue I faced with DynamicCode is that apparently references to mobjects in manim are not guaranteed to remain valid after an animation. I'm relatively new to Manim, so if I'm doing something wrong, someone can please correct me. But I found that the glyph references after animating changes in DynamicCode were stale, and I had to use string and duck tape to go find the new valid glyph references within All in all, I think the issues popping up here point to larger problems with Manim's design (again, I'm a newbie so I could be wrong), but from reading the docs/discussions I think that at least some if not all of these issues are known to the community but there appears to be little energy for anyone to undertake the gargantuan task of restructuring Manim from the ground up. So maybe hacky solutions like DynamicCode are what is required in the meantime? Or maybe there's a smarter solution you can come up with. You may also want to have a look at alternative animation libraries such as Motion Canvas which offers nice Code diffing out of the box. In my off the cuff and not well thought out opinion, I think Manim is still nice in comparison to alternatives like Motion Canvas for quick and dirty simple animations. However, if some of the fundamental design flaws are not addressed, I anticipate that things like Motion Canvas will make Manim moot in the not too distant future. Also, if you use VSCode, you should check out my Manim Viewer extension. Cheers |
thanks for your nice feedback and direction! :) |
codefrom manim import *
def find_line_matches(before: Code, after: Code):
before_lines = [
line.lstrip() if line.strip() != "" else None
for line in before.code_string.splitlines()
]
after_lines = [
line.lstrip() if line.strip() != "" else None
for line in after.code_string.splitlines()
]
matches = []
for i, b_line in enumerate(before_lines):
if b_line is None:
continue
for j, a_line in enumerate(after_lines):
if a_line is not None and b_line == a_line:
matches.append((i, j))
before_lines[i] = None
after_lines[j] = None
break
deletions = []
for i, line in enumerate(before_lines):
if before_lines[i] is not None:
deletions.append((i, len(line)))
additions = []
for j, line in enumerate(after_lines):
if after_lines[j] is not None:
additions.append((j, len(line)))
return matches, deletions, additions
class CodeTransform(AnimationGroup):
def __init__(self, before: Code, after: Code, **kwargs):
matches, deletions, additions = find_line_matches(before, after)
transform_pairs = [
(before.code[i], after.code[j])
for i, j in matches
]
delete_lines = [before.code[i] for i, _ in deletions]
add_lines = [after.code[j] for j, _ in additions]
animations = []
if hasattr(before, 'background_mobject') and hasattr(after, 'background_mobject'):
animations.append(
Transform(before.background_mobject, after.background_mobject)
)
if hasattr(before, 'line_numbers') and hasattr(after, 'line_numbers'):
animations.append(
Transform(before.line_numbers, after.line_numbers)
)
if delete_lines:
animations.append(FadeOut(*delete_lines))
if transform_pairs:
animations.append(LaggedStart(*[
Transform(before_line, after_line)
for before_line, after_line in transform_pairs
]))
if add_lines:
animations.append(FadeIn(*add_lines))
super().__init__(
*animations,
group=None,
run_time=None,
rate_func=linear,
lag_ratio=0.0,
**kwargs,
)
def load_code(code=None):
code_block = Code(
code=code,
tab_width=4,
background="window",
language="Python",
font="Monospace",
style="one-dark",
line_spacing=1
).scale(0.8)
return code_block
class Test(Scene):
def construct(self):
before_code = '''from manim import *
class Animation(Scene):
def construct(self):
square = Square(side_length=2.0, color=RED)
square.shift(LEFT * 2)
self.play(Create(square))
self.wait()
'''
after_code = '''from manim import *
class Animation(Scene):
def construct(self):
circle = Circle(radius=1.0, color=BLUE)
circle.shift(RIGHT * 2)
square = Square(side_length=2.0, color=RED)
square.shift(LEFT * 2)
self.play(Create(circle))
self.wait()
'''
before = load_code(code=before_code)
after = load_code(code=after_code)
self.add(before)
self.wait()
self.play(CodeTransform(before, after))
self.wait() videoTest.mp4it works good. I'm currently trying to implement While searching, I found a pretty decent code snippet. After running it, I noticed that It works better than I expected, and for now, it seems like a good temporary replacement for the terrible animation of the I’d like to close this issue with the current code. But I really like your + Oh, and your Manim Viewer is really useful. Thanks :) |
I wouldn't close this issue as I think this is still a big problem in Manim. For example, does your solution handle inserting/removing characters in the middle of a line? |
I agree. Since this is my first open-source contribution, I didn't fully understand the process. Thank you for the feedback. |
Also, nice job finding that code snippet above. Maybe a custom Transform class rather than a custom Mobject is the way to go. |
unexpected behavior
When using Manim's
Code
object to animate code changes, animations are applied to the entire code block, even if only a single line is added.Video
CodeAnimation.mp4
Expected behavior
The animation should be applied only to the newly added or modified line of code.
Actual behavior
The animation is applied to the entire code block, including unchanged lines, making it unclear which part of the code was modified.
Code:
System specifications
The text was updated successfully, but these errors were encountered: