ManimCommunity / ManimCommunity/manim
Starting Animations using Scene.play while others are running
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 40.9k
- Forks
- 3.1k
- Avg merge
- 3d 12h
- Merged PRs (30d)
- 25
Description
First of all: This might just as well be a bug report - I am unsure...
## Motivation
Look at the following scene:
https://user-images.githubusercontent.com/26899845/113640645-3ab53f80-967c-11eb-8789-4f3832acf0a1.mp4
Swiping a line across the Scene and playing a Flash as soon as it hits a dot... Or more generally: Animate one thing and play additional animations based on the state of the animated thing. Sounds like a job for an update function. Consider the following code:
```py
class UsingUpdater(Scene):
def construct(self):
#Setup
dots = VGroup(*[Dot(RIGHT*random.randrange(-6, 6)+UP*random.randrange(-2, 2)) for i in range(10)])
line = Line(UP*3, DOWN*3).align_on_border(LEFT)
self.add(dots)
self.play(Create(line))
#Updater
def flash_hit_dots(mob):
for dot in dots:
if dot.get_center()[0] < mob.get_center()[0] and not hasattr(dot, "marked"):
self.play(Flash(dot, run_time=0.5))
dot.marked = True
line.add_updater(flash_hit_dots)
#Animate Line
self.play(line.animate.align_on_border(RIGHT), run_time=5)
self.wait()
```
However, this results in `ValueError: write to closed file`.
## Description of proposed feature
The feature request is quite simple, but I think it is hard to implement: Allow using `Scene.play()` while an Animation is running.
## Additional comments
I have seen quite a few different workarounds for this problem, even in 3b1b's code. Some are based on splitting the animation at each new event, others are based on precalculating all timings in advance - but all of them have one thing in common: They are tedious and error-prone. In case you are wondering, how I created the example Scene: Have a look at this mess...
```py
class Workaround(Scene):
def construct(self):
#Setup
dots = VGroup(*[Dot(RIGHT*random.randrange(-6, 6)+UP*random.randrange(-2, 2)) for i in range(10)])
line = Line(UP*3, DOWN*3).align_on_border(LEFT)
self.add(dots)
self.play(Create(line))
run_time = 5
rate_func = smooth
fps = config["frame_rate"]
#First pass to determine frames to start the flash:
animations = []
frames = run_time*fps
x_0 = line.get_center()[0]
x_1 = line.copy().align_on_border(RIGHT).get_center()[0]
for t in range(frames):
alpha = rate_func(t/frames)
x = x_0 + alpha*(x_1-x_0)
for dot in dots:
if dot.get_center()[0] < x and not hasattr(dot, "marked"):
#create dummy wait animation:
wait = FadeOut(Mobject(), run_time=t/fps)
animations.append(AnimationGroup(wait, Flash(dot, run_time=0.5), lag_ratio=1))
dot.marked = True
#Run the animations
self.play(AnimationGroup(
ApplyMethod(line.align_on_border, RIGHT, run_time=run_time, rate_func=rate_func),
*animations
))
self.wait()
```
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at Scene.play and the updater callback in the provided UsingUpdater example; reproduce the write-to-closed-file failure. Done means an animation can trigger Scene.play while another animation is running without that error, with the example's Flash events occurring as the line crosses dots.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- computer-graphics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100