Skip to content

Commit 455f104

Browse files
authored
fix(present): finish up state machine (#234)
1 parent 9e1e0f2 commit 455f104

File tree

1 file changed

+47
-40
lines changed

1 file changed

+47
-40
lines changed

manim_slides/present.py

Lines changed: 47 additions & 40 deletions
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

@@ -60,16 +60,20 @@ class AspectRatio(Enum):
6060

6161

6262
@unique
63-
class State(IntEnum):
63+
class State(IntFlag):
6464
"""Represents all possible states of a slide presentation."""
6565

66+
"""A video is actively being played."""
6667
PLAYING = auto()
68+
"""A video was manually paused."""
6769
PAUSED = auto()
70+
"""Waiting for user to press next (or else)."""
6871
WAIT = auto()
72+
"""Presentation was terminated."""
6973
END = auto()
7074

7175
def __str__(self) -> str:
72-
return self.name.capitalize()
76+
return self.name.capitalize() # type: ignore
7377

7478

7579
def now() -> float:
@@ -279,10 +283,10 @@ def reset(self) -> None:
279283

280284
def load_last_slide(self) -> None:
281285
"""Loads last slide."""
282-
self.current_slide_index = len(self.slides) - 2
286+
self.current_slide_index = len(self.slides) - 1
283287
assert (
284288
self.current_slide_index >= 0
285-
), "Slides should be at list of a least two elements"
289+
), "Slides should be at list of a least one element"
286290
self.current_animation = self.current_slide.start_animation
287291
self.load_animation_cap(self.current_animation)
288292
self.slides[-1].terminated = False
@@ -315,41 +319,37 @@ def update_state(self, state: State) -> Tuple[np.ndarray, State]:
315319
It does this by reading the video information and checking if the state is still correct.
316320
It returns the frame to show (lastframe) and the new state.
317321
"""
318-
if state == State.PAUSED:
322+
if state ^ State.PLAYING: # If not playing, we return the same
319323
if self.lastframe is None:
320324
_, self.lastframe = self.current_cap.read()
321325
return self.lastframe, state
326+
322327
still_playing, frame = self.current_cap.read()
328+
323329
if still_playing:
324330
self.lastframe = frame
325-
elif state == state.WAIT or state == state.PAUSED: # type: ignore
326-
return self.lastframe, state
327-
elif self.current_slide.is_last() and self.current_slide.terminated:
328-
return self.lastframe, State.END
329-
else: # not still playing
330-
if self.is_last_animation:
331-
if self.current_slide.is_slide():
331+
return self.lastframe, State.PLAYING
332+
333+
# Video was terminated
334+
if self.is_last_animation:
335+
if self.current_slide.is_loop():
336+
if self.reverse:
332337
state = State.WAIT
333-
elif self.current_slide.is_loop():
334-
if self.reverse:
335-
state = State.WAIT
336-
else:
337-
self.current_animation = self.current_slide.start_animation
338-
state = State.PLAYING
339-
self.rewind_current_slide()
340-
elif self.current_slide.is_last():
341-
self.current_slide.terminated = True
342-
elif (
343-
self.current_slide.is_last()
344-
and self.current_slide.end_animation == self.current_animation
345-
):
346-
state = State.WAIT
338+
339+
else:
340+
self.current_animation = self.current_slide.start_animation
341+
state = State.PLAYING
342+
self.rewind_current_slide()
343+
elif self.current_slide.is_last():
344+
state = State.END
347345
else:
348-
# Play next video!
349-
self.current_animation = self.next_animation
350-
self.load_animation_cap(self.current_animation)
351-
# Reset video to position zero if it has been played before
352-
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
346+
state = State.WAIT
347+
else:
348+
# Play next video!
349+
self.current_animation = self.next_animation
350+
self.load_animation_cap(self.current_animation)
351+
# Reset video to position zero if it has been played before
352+
self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
353353

354354
return self.lastframe, state
355355

@@ -431,6 +431,11 @@ def current_background_color(self) -> Color:
431431
"""Returns the background color of the current presentation."""
432432
return self.current_presentation.background_color
433433

434+
@property
435+
def is_last_presentation(self) -> bool:
436+
"""Returns True if current presentation is the last one."""
437+
return self.current_presentation_index == len(self) - 1
438+
434439
def start(self) -> None:
435440
super().start()
436441
self.change_presentation_signal.emit()
@@ -442,18 +447,15 @@ def run(self) -> None:
442447
self.lastframe, self.state = self.current_presentation.update_state(
443448
self.state
444449
)
445-
if self.state == State.PLAYING or self.state == State.PAUSED:
450+
if self.state & (State.PLAYING | State.PAUSED):
446451
if self.start_paused:
447452
self.state = State.PAUSED
448453
self.start_paused = False
449-
if self.state == State.END:
454+
if self.state & State.END:
450455
if self.current_presentation_index == len(self.presentations) - 1:
451456
if self.exit_after_last_slide:
452457
self.run_flag = False
453458
continue
454-
else:
455-
self.current_presentation_index += 1
456-
self.state = State.PLAYING
457459

458460
self.handle_key()
459461
self.show_video()
@@ -560,10 +562,14 @@ def handle_key(self) -> None:
560562
self.state = State.PAUSED
561563
elif self.state == State.PAUSED and keys.PLAY_PAUSE.match(key):
562564
self.state = State.PLAYING
563-
elif self.state == State.WAIT and (
564-
keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key)
565+
elif self.state & (State.END | State.WAIT) and (
566+
keys.CONTINUE.match(key) or keys.PLAY_PAUSE.match(key) or self.skip_all
565567
):
566-
self.current_presentation.load_next_slide()
568+
if (self.state & State.END) and not self.is_last_presentation:
569+
self.current_presentation_index += 1
570+
self.current_presentation.rewind_current_slide()
571+
else:
572+
self.current_presentation.load_next_slide()
567573
self.state = State.PLAYING
568574
elif (
569575
self.state == State.PLAYING and keys.CONTINUE.match(key)
@@ -574,6 +580,7 @@ def handle_key(self) -> None:
574580
if self.current_presentation_index == 0:
575581
self.current_presentation.load_previous_slide()
576582
else:
583+
self.current_presentation.cancel_reverse()
577584
self.current_presentation_index -= 1
578585
self.current_presentation.load_last_slide()
579586
self.state = State.PLAYING

0 commit comments

Comments
 (0)