Skip to content

Commit 0f9048a

Browse files
committed
fix(present): finish up state machine
1 parent aace5de commit 0f9048a

File tree

1 file changed

+47
-40
lines changed

1 file changed

+47
-40
lines changed

manim_slides/present.py

+47-40
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import signal
44
import sys
55
import time
6-
from enum import Enum, IntEnum, auto, unique
6+
from enum import Enum, IntFlag, auto, unique
77
from pathlib import Path
88
from typing import Any, Dict, List, Optional, Tuple, Union
99

@@ -52,16 +52,20 @@ class AspectRatio(Enum):
5252

5353

5454
@unique
55-
class State(IntEnum):
55+
class State(IntFlag):
5656
"""Represents all possible states of a slide presentation."""
5757

58+
"""A video is actively being played."""
5859
PLAYING = auto()
60+
"""A video was manually paused."""
5961
PAUSED = auto()
62+
"""Waiting for user to press next (or else)."""
6063
WAIT = auto()
64+
"""Presentation was terminated."""
6165
END = auto()
6266

6367
def __str__(self) -> str:
64-
return self.name.capitalize()
68+
return self.name.capitalize() # type: ignore
6569

6670

6771
def now() -> float:
@@ -271,10 +275,10 @@ def reset(self) -> None:
271275

272276
def load_last_slide(self) -> None:
273277
"""Loads last slide."""
274-
self.current_slide_index = len(self.slides) - 2
278+
self.current_slide_index = len(self.slides) - 1
275279
assert (
276280
self.current_slide_index >= 0
277-
), "Slides should be at list of a least two elements"
281+
), "Slides should be at list of a least one element"
278282
self.current_animation = self.current_slide.start_animation
279283
self.load_animation_cap(self.current_animation)
280284
self.slides[-1].terminated = False
@@ -307,41 +311,37 @@ def update_state(self, state: State) -> Tuple[np.ndarray, State]:
307311
It does this by reading the video information and checking if the state is still correct.
308312
It returns the frame to show (lastframe) and the new state.
309313
"""
310-
if state == State.PAUSED:
314+
if state ^ State.PLAYING: # If not playing, we return the same
311315
if self.lastframe is None:
312316
_, self.lastframe = self.current_cap.read()
313317
return self.lastframe, state
318+
314319
still_playing, frame = self.current_cap.read()
320+
315321
if still_playing:
316322
self.lastframe = frame
317-
elif state == state.WAIT or state == state.PAUSED: # type: ignore
318-
return self.lastframe, state
319-
elif self.current_slide.is_last() and self.current_slide.terminated:
320-
return self.lastframe, State.END
321-
else: # not still playing
322-
if self.is_last_animation:
323-
if self.current_slide.is_slide():
323+
return self.lastframe, State.PLAYING
324+
325+
# Video was terminated
326+
if self.is_last_animation:
327+
if self.current_slide.is_loop():
328+
if self.reverse:
324329
state = State.WAIT
325-
elif self.current_slide.is_loop():
326-
if self.reverse:
327-
state = State.WAIT
328-
else:
329-
self.current_animation = self.current_slide.start_animation
330-
state = State.PLAYING
331-
self.rewind_current_slide()
332-
elif self.current_slide.is_last():
333-
self.current_slide.terminated = True
334-
elif (
335-
self.current_slide.is_last()
336-
and self.current_slide.end_animation == self.current_animation
337-
):
338-
state = State.WAIT
330+
331+
else:
332+
self.current_animation = self.current_slide.start_animation
333+
state = State.PLAYING
334+
self.rewind_current_slide()
335+
elif self.current_slide.is_last():
336+
state = State.END
339337
else:
340-
# Play next video!
341-
self.current_animation = self.next_animation
342-
self.load_animation_cap(self.current_animation)
343-
# Reset video to position zero if it has been played before
344-
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
338+
state = State.WAIT
339+
else:
340+
# Play next video!
341+
self.current_animation = self.next_animation
342+
self.load_animation_cap(self.current_animation)
343+
# Reset video to position zero if it has been played before
344+
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
345345

346346
return self.lastframe, state
347347

@@ -423,25 +423,27 @@ def current_background_color(self) -> Color:
423423
"""Returns the background color of the current presentation."""
424424
return self.current_presentation.background_color
425425

426+
@property
427+
def is_last_presentation(self) -> bool:
428+
"""Returns True if current presentation is the last one."""
429+
return self.current_presentation_index == len(self) - 1
430+
426431
def run(self) -> None:
427432
"""Runs a series of presentations until end or exit."""
428433
while self.run_flag:
429434
last_time = now()
430435
self.lastframe, self.state = self.current_presentation.update_state(
431436
self.state
432437
)
433-
if self.state == State.PLAYING or self.state == State.PAUSED:
438+
if self.state & (State.PLAYING | State.PAUSED):
434439
if self.start_paused:
435440
self.state = State.PAUSED
436441
self.start_paused = False
437-
if self.state == State.END:
442+
if self.state & State.END:
438443
if self.current_presentation_index == len(self.presentations) - 1:
439444
if self.exit_after_last_slide:
440445
self.run_flag = False
441446
continue
442-
else:
443-
self.current_presentation_index += 1
444-
self.state = State.PLAYING
445447

446448
self.handle_key()
447449
self.show_video()
@@ -548,10 +550,14 @@ def handle_key(self) -> None:
548550
self.state = State.PAUSED
549551
elif self.state == State.PAUSED and keys.PLAY_PAUSE.match(key):
550552
self.state = State.PLAYING
551-
elif self.state == State.WAIT and (
552-
keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key)
553+
elif self.state & (State.END | State.WAIT) and (
554+
keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key) or self.skip_all
553555
):
554-
self.current_presentation.load_next_slide()
556+
if (self.state & State.END) and not self.is_last_presentation:
557+
self.current_presentation_index += 1
558+
self.current_presentation.rewind_current_slide()
559+
else:
560+
self.current_presentation.load_next_slide()
555561
self.state = State.PLAYING
556562
elif (
557563
self.state == State.PLAYING and keys.CONTINUE.match(key)
@@ -562,6 +568,7 @@ def handle_key(self) -> None:
562568
if self.current_presentation_index == 0:
563569
self.current_presentation.load_previous_slide()
564570
else:
571+
self.current_presentation.cancel_reverse()
565572
self.current_presentation_index -= 1
566573
self.current_presentation.load_last_slide()
567574
self.state = State.PLAYING

0 commit comments

Comments
 (0)