Skip to content

Scene

Module: terminaltexteffects.engine.animation

A Scene is a collection of Frames that can be played in sequence. Scenes can be looped and synced to movement.

Parameters:

Name Type Description Default
scene_id str

the ID of the Scene

required
is_looping bool

Whether the Scene should loop. Defaults to False.

False
sync SyncMetric | None

The type of sync to use for the Scene. Defaults to None.

None
ease EasingFunction | None

The easing function to use for the Scene. Defaults to None.

None
no_color bool

Whether to ignore colors. Defaults to False.

False
use_xterm_colors bool

Whether to convert all colors to XTerm-256 colors. Defaults to False.

False

Methods:

Name Description
add_frame

Adds a Frame to the Scene.

activate

Activates the Scene.

get_next_visual

Gets the next CharacterVisual in the Scene.

apply_gradient_to_symbols

Applies a gradient effect to a sequence of symbols.

reset_scene

Resets the Scene.

Attributes:

Name Type Description
scene_id str

the ID of the Scene

is_looping bool

Whether the Scene should loop

sync SyncMetric | None

The type of sync to use for the Scene

ease EasingFunction | None

The easing function to use for the Scene

no_color bool

Whether to ignore colors

use_xterm_colors bool

Whether to convert all colors to XTerm-256 colors

frames list[Frame]

The list of Frames in the Scene

played_frames list[Frame]

The list of Frames that have been played

frame_index_map dict[int, Frame]

A mapping of frame index to Frame

easing_total_steps int

The total number of steps in the easing function

easing_current_step int

The current step in the easing function

Source code in terminaltexteffects/engine/animation.py
class Scene:
    """A Scene is a collection of Frames that can be played in sequence. Scenes can be looped and synced to movement.

    Args:
        scene_id (str): the ID of the Scene
        is_looping (bool, optional): Whether the Scene should loop. Defaults to False.
        sync (SyncMetric | None, optional): The type of sync to use for the Scene. Defaults to None.
        ease (easing.EasingFunction | None, optional): The easing function to use for the Scene. Defaults to None.
        no_color (bool, optional): Whether to ignore colors. Defaults to False.
        use_xterm_colors (bool, optional): Whether to convert all colors to XTerm-256 colors. Defaults to False.

    Methods:
        add_frame: Adds a Frame to the Scene.
        activate: Activates the Scene.
        get_next_visual: Gets the next CharacterVisual in the Scene.
        apply_gradient_to_symbols: Applies a gradient effect to a sequence of symbols.
        reset_scene: Resets the Scene.

    Attributes:
        scene_id (str): the ID of the Scene
        is_looping (bool): Whether the Scene should loop
        sync (SyncMetric | None): The type of sync to use for the Scene
        ease (easing.EasingFunction | None): The easing function to use for the Scene
        no_color (bool): Whether to ignore colors
        use_xterm_colors (bool): Whether to convert all colors to XTerm-256 colors
        frames (list[Frame]): The list of Frames in the Scene
        played_frames (list[Frame]): The list of Frames that have been played
        frame_index_map (dict[int, Frame]): A mapping of frame index to Frame
        easing_total_steps (int): The total number of steps in the easing function
        easing_current_step (int): The current step in the easing function
    """

    xterm_color_map: dict[str, int] = {}

    def __init__(
        self,
        scene_id: str,
        is_looping: bool = False,
        sync: SyncMetric | None = None,
        ease: easing.EasingFunction | None = None,
        no_color: bool = False,
        use_xterm_colors: bool = False,
    ):
        """Initializes a Scene.

        Args:
            scene_id (str): the ID of the Scene
            is_looping (bool, optional): Whether the Scene should loop. Defaults to False.
            sync (SyncMetric | None, optional): The type of sync to use for the Scene. Defaults to None.
            ease (easing.EasingFunction | None, optional): The easing function to use for the Scene. Defaults to None.
            no_color (bool, optional): Whether to colors should be ignored. Defaults to False.
            use_xterm_colors (bool, optional): Whether to convert all colors to XTerm-256 colors. Defaults to False.
        """
        self.scene_id = scene_id
        self.is_looping = is_looping
        self.sync: SyncMetric | None = sync
        self.ease: easing.EasingFunction | None = ease
        self.no_color = no_color
        self.use_xterm_colors = use_xterm_colors
        self.frames: list[Frame] = []
        self.played_frames: list[Frame] = []
        self.frame_index_map: dict[int, Frame] = {}
        self.easing_total_steps: int = 0
        self.easing_current_step: int = 0

    def add_frame(
        self,
        symbol: str,
        duration: int,
        *,
        color: graphics.Color | None = None,
        bold=False,
        dim=False,
        italic=False,
        underline=False,
        blink=False,
        reverse=False,
        hidden=False,
        strike=False,
    ) -> None:
        """Adds a Frame to the Scene with the given symbol, duration, color, and graphical modes.

        Args:
            symbol (str): the symbol to show
            duration (int): the number of frames to use the Frame
            color (graphics.Color, optional): the color to show. Defaults to None.
            bold (bool, optional): bold mode. Defaults to False.
            dim (bool, optional): dim mode. Defaults to False.
            italic (bool, optional): italic mode. Defaults to False.
            underline (bool, optional): underline mode. Defaults to False.
            blink (bool, optional): blink mode. Defaults to False.
            reverse (bool, optional): reverse mode. Defaults to False.
            hidden (bool, optional): hidden mode. Defaults to False.
            strike (bool, optional): strike mode. Defaults to False.
        """
        char_vis_color: str | int | None = None
        if color:
            if self.no_color:
                char_vis_color = None
            elif self.use_xterm_colors:
                if color.xterm_color:
                    char_vis_color = color.xterm_color
                elif color.rgb_color in self.xterm_color_map:
                    char_vis_color = self.xterm_color_map[color.rgb_color]
                else:
                    xterm_color = hexterm.hex_to_xterm(color.rgb_color)
                    self.xterm_color_map[color.rgb_color] = xterm_color
                    char_vis_color = xterm_color
            else:
                char_vis_color = color.rgb_color
        if duration < 1:
            raise ValueError("duration must be greater than 0")
        char_vis = CharacterVisual(
            symbol,
            bold=bold,
            dim=dim,
            italic=italic,
            underline=underline,
            blink=blink,
            reverse=reverse,
            hidden=hidden,
            strike=strike,
            color=color,
            _color_code=char_vis_color,
        )
        frame = Frame(char_vis, duration)
        self.frames.append(frame)
        for _ in range(frame.duration):
            self.frame_index_map[self.easing_total_steps] = frame
            self.easing_total_steps += 1

    def activate(self) -> CharacterVisual:
        """Activates the Scene by returning the first frame CharacterVisual. Called by the Animation object when the Scene is activated.

        Raises:
            ValueError: if the Scene has no frames

        Returns:
            CharacterVisual: the next CharacterVisual in the Scene
        """
        if self.frames:
            return self.frames[0].character_visual
        else:
            raise ValueError("Scene has no frames.")

    def get_next_visual(self) -> CharacterVisual:
        """
        This method is used to get the next CharacterVisual in the Scene. It first retrieves the current frame from the frames list.
        It then increments the ticks_elapsed attribute of the Frame. If the ticks_elapsed equals the duration
        of the current frame, it resets ticks_elapsed to 0 and moves the current frame from the frames list to the
        played_frames list. If the Scene is set to loop and all frames have been played, it refills the frames list with the
        frames from played_frames and clears played_frames. Finally, it returns the CharacterVisual of the current frame.

        Returns:
            CharacterVisual: The visual of the current frame in the Scene.
        """

        current_frame = self.frames[0]
        next_visual = current_frame.character_visual
        current_frame.ticks_elapsed += 1
        if current_frame.ticks_elapsed == current_frame.duration:
            current_frame.ticks_elapsed = 0
            self.played_frames.append(self.frames.pop(0))
            if self.is_looping and not self.frames:
                self.frames.extend(self.played_frames)
                self.played_frames.clear()
        return next_visual

    def apply_gradient_to_symbols(
        self, gradient: graphics.Gradient, symbols: typing.Sequence[str], duration: int
    ) -> None:
        """
        Applies a gradient effect to a sequence of symbols and adds each symbol as a frame to the Scene.

        This method works by iterating over the symbols and calculating a progress ratio for each symbol.
        This ratio is calculated by dividing the index of the current symbol (plus one) by the total number of symbols.
        The gradient index is then calculated by multiplying the symbol progress by the length of the gradient's spectrum.
        This index is used to select a color from the gradient's spectrum.

        The method then iterates over the colors in the gradient's spectrum from the last index to the gradient index
        (or 1 if gradient index is 0). For each color, it calls the add_frame method, passing the symbol, duration, and color.
        This adds a frame to the Scene with the symbol displayed in the color from the gradient.

        Finally, the last index is updated to the current gradient index, and the process repeats for the next symbol.
        This results in each symbol being displayed in a sequence of colors from the gradient, creating a gradient effect across the symbols.

        Args:
            gradient (graphics.Gradient): The gradient to apply.
            symbols (list[str]): The list of symbols to apply the gradient to.
            duration (int): The duration to show each frame.

        Returns:
            None
        """
        last_index = 0
        for symbol_index, symbol in enumerate(symbols):
            symbol_progress = (symbol_index + 1) / len(symbols)
            gradient_index = int(symbol_progress * len(gradient.spectrum))
            for color in gradient.spectrum[last_index : max(gradient_index, 1)]:
                self.add_frame(symbol, duration, color=color)
            last_index = gradient_index

    def reset_scene(self) -> None:
        """Resets the Scene."""
        for sequence in self.frames:
            sequence.ticks_elapsed = 0
            self.played_frames.append(sequence)
        self.frames.clear()
        self.frames.extend(self.played_frames)
        self.played_frames.clear()

    def __eq__(self, other: typing.Any):
        if not isinstance(other, Scene):
            return NotImplemented
        return self.scene_id == other.scene_id

    def __hash__(self):
        return hash(self.scene_id)

__init__(scene_id, is_looping=False, sync=None, ease=None, no_color=False, use_xterm_colors=False)

Initializes a Scene.

Parameters:

Name Type Description Default
scene_id str

the ID of the Scene

required
is_looping bool

Whether the Scene should loop. Defaults to False.

False
sync SyncMetric | None

The type of sync to use for the Scene. Defaults to None.

None
ease EasingFunction | None

The easing function to use for the Scene. Defaults to None.

None
no_color bool

Whether to colors should be ignored. Defaults to False.

False
use_xterm_colors bool

Whether to convert all colors to XTerm-256 colors. Defaults to False.

False
Source code in terminaltexteffects/engine/animation.py
def __init__(
    self,
    scene_id: str,
    is_looping: bool = False,
    sync: SyncMetric | None = None,
    ease: easing.EasingFunction | None = None,
    no_color: bool = False,
    use_xterm_colors: bool = False,
):
    """Initializes a Scene.

    Args:
        scene_id (str): the ID of the Scene
        is_looping (bool, optional): Whether the Scene should loop. Defaults to False.
        sync (SyncMetric | None, optional): The type of sync to use for the Scene. Defaults to None.
        ease (easing.EasingFunction | None, optional): The easing function to use for the Scene. Defaults to None.
        no_color (bool, optional): Whether to colors should be ignored. Defaults to False.
        use_xterm_colors (bool, optional): Whether to convert all colors to XTerm-256 colors. Defaults to False.
    """
    self.scene_id = scene_id
    self.is_looping = is_looping
    self.sync: SyncMetric | None = sync
    self.ease: easing.EasingFunction | None = ease
    self.no_color = no_color
    self.use_xterm_colors = use_xterm_colors
    self.frames: list[Frame] = []
    self.played_frames: list[Frame] = []
    self.frame_index_map: dict[int, Frame] = {}
    self.easing_total_steps: int = 0
    self.easing_current_step: int = 0

activate()

Activates the Scene by returning the first frame CharacterVisual. Called by the Animation object when the Scene is activated.

Raises:

Type Description
ValueError

if the Scene has no frames

Returns:

Name Type Description
CharacterVisual CharacterVisual

the next CharacterVisual in the Scene

Source code in terminaltexteffects/engine/animation.py
def activate(self) -> CharacterVisual:
    """Activates the Scene by returning the first frame CharacterVisual. Called by the Animation object when the Scene is activated.

    Raises:
        ValueError: if the Scene has no frames

    Returns:
        CharacterVisual: the next CharacterVisual in the Scene
    """
    if self.frames:
        return self.frames[0].character_visual
    else:
        raise ValueError("Scene has no frames.")

add_frame(symbol, duration, *, color=None, bold=False, dim=False, italic=False, underline=False, blink=False, reverse=False, hidden=False, strike=False)

Adds a Frame to the Scene with the given symbol, duration, color, and graphical modes.

Parameters:

Name Type Description Default
symbol str

the symbol to show

required
duration int

the number of frames to use the Frame

required
color Color

the color to show. Defaults to None.

None
bold bool

bold mode. Defaults to False.

False
dim bool

dim mode. Defaults to False.

False
italic bool

italic mode. Defaults to False.

False
underline bool

underline mode. Defaults to False.

False
blink bool

blink mode. Defaults to False.

False
reverse bool

reverse mode. Defaults to False.

False
hidden bool

hidden mode. Defaults to False.

False
strike bool

strike mode. Defaults to False.

False
Source code in terminaltexteffects/engine/animation.py
def add_frame(
    self,
    symbol: str,
    duration: int,
    *,
    color: graphics.Color | None = None,
    bold=False,
    dim=False,
    italic=False,
    underline=False,
    blink=False,
    reverse=False,
    hidden=False,
    strike=False,
) -> None:
    """Adds a Frame to the Scene with the given symbol, duration, color, and graphical modes.

    Args:
        symbol (str): the symbol to show
        duration (int): the number of frames to use the Frame
        color (graphics.Color, optional): the color to show. Defaults to None.
        bold (bool, optional): bold mode. Defaults to False.
        dim (bool, optional): dim mode. Defaults to False.
        italic (bool, optional): italic mode. Defaults to False.
        underline (bool, optional): underline mode. Defaults to False.
        blink (bool, optional): blink mode. Defaults to False.
        reverse (bool, optional): reverse mode. Defaults to False.
        hidden (bool, optional): hidden mode. Defaults to False.
        strike (bool, optional): strike mode. Defaults to False.
    """
    char_vis_color: str | int | None = None
    if color:
        if self.no_color:
            char_vis_color = None
        elif self.use_xterm_colors:
            if color.xterm_color:
                char_vis_color = color.xterm_color
            elif color.rgb_color in self.xterm_color_map:
                char_vis_color = self.xterm_color_map[color.rgb_color]
            else:
                xterm_color = hexterm.hex_to_xterm(color.rgb_color)
                self.xterm_color_map[color.rgb_color] = xterm_color
                char_vis_color = xterm_color
        else:
            char_vis_color = color.rgb_color
    if duration < 1:
        raise ValueError("duration must be greater than 0")
    char_vis = CharacterVisual(
        symbol,
        bold=bold,
        dim=dim,
        italic=italic,
        underline=underline,
        blink=blink,
        reverse=reverse,
        hidden=hidden,
        strike=strike,
        color=color,
        _color_code=char_vis_color,
    )
    frame = Frame(char_vis, duration)
    self.frames.append(frame)
    for _ in range(frame.duration):
        self.frame_index_map[self.easing_total_steps] = frame
        self.easing_total_steps += 1

apply_gradient_to_symbols(gradient, symbols, duration)

Applies a gradient effect to a sequence of symbols and adds each symbol as a frame to the Scene.

This method works by iterating over the symbols and calculating a progress ratio for each symbol. This ratio is calculated by dividing the index of the current symbol (plus one) by the total number of symbols. The gradient index is then calculated by multiplying the symbol progress by the length of the gradient's spectrum. This index is used to select a color from the gradient's spectrum.

The method then iterates over the colors in the gradient's spectrum from the last index to the gradient index (or 1 if gradient index is 0). For each color, it calls the add_frame method, passing the symbol, duration, and color. This adds a frame to the Scene with the symbol displayed in the color from the gradient.

Finally, the last index is updated to the current gradient index, and the process repeats for the next symbol. This results in each symbol being displayed in a sequence of colors from the gradient, creating a gradient effect across the symbols.

Parameters:

Name Type Description Default
gradient Gradient

The gradient to apply.

required
symbols list[str]

The list of symbols to apply the gradient to.

required
duration int

The duration to show each frame.

required

Returns:

Type Description
None

None

Source code in terminaltexteffects/engine/animation.py
def apply_gradient_to_symbols(
    self, gradient: graphics.Gradient, symbols: typing.Sequence[str], duration: int
) -> None:
    """
    Applies a gradient effect to a sequence of symbols and adds each symbol as a frame to the Scene.

    This method works by iterating over the symbols and calculating a progress ratio for each symbol.
    This ratio is calculated by dividing the index of the current symbol (plus one) by the total number of symbols.
    The gradient index is then calculated by multiplying the symbol progress by the length of the gradient's spectrum.
    This index is used to select a color from the gradient's spectrum.

    The method then iterates over the colors in the gradient's spectrum from the last index to the gradient index
    (or 1 if gradient index is 0). For each color, it calls the add_frame method, passing the symbol, duration, and color.
    This adds a frame to the Scene with the symbol displayed in the color from the gradient.

    Finally, the last index is updated to the current gradient index, and the process repeats for the next symbol.
    This results in each symbol being displayed in a sequence of colors from the gradient, creating a gradient effect across the symbols.

    Args:
        gradient (graphics.Gradient): The gradient to apply.
        symbols (list[str]): The list of symbols to apply the gradient to.
        duration (int): The duration to show each frame.

    Returns:
        None
    """
    last_index = 0
    for symbol_index, symbol in enumerate(symbols):
        symbol_progress = (symbol_index + 1) / len(symbols)
        gradient_index = int(symbol_progress * len(gradient.spectrum))
        for color in gradient.spectrum[last_index : max(gradient_index, 1)]:
            self.add_frame(symbol, duration, color=color)
        last_index = gradient_index

get_next_visual()

This method is used to get the next CharacterVisual in the Scene. It first retrieves the current frame from the frames list. It then increments the ticks_elapsed attribute of the Frame. If the ticks_elapsed equals the duration of the current frame, it resets ticks_elapsed to 0 and moves the current frame from the frames list to the played_frames list. If the Scene is set to loop and all frames have been played, it refills the frames list with the frames from played_frames and clears played_frames. Finally, it returns the CharacterVisual of the current frame.

Returns:

Name Type Description
CharacterVisual CharacterVisual

The visual of the current frame in the Scene.

Source code in terminaltexteffects/engine/animation.py
def get_next_visual(self) -> CharacterVisual:
    """
    This method is used to get the next CharacterVisual in the Scene. It first retrieves the current frame from the frames list.
    It then increments the ticks_elapsed attribute of the Frame. If the ticks_elapsed equals the duration
    of the current frame, it resets ticks_elapsed to 0 and moves the current frame from the frames list to the
    played_frames list. If the Scene is set to loop and all frames have been played, it refills the frames list with the
    frames from played_frames and clears played_frames. Finally, it returns the CharacterVisual of the current frame.

    Returns:
        CharacterVisual: The visual of the current frame in the Scene.
    """

    current_frame = self.frames[0]
    next_visual = current_frame.character_visual
    current_frame.ticks_elapsed += 1
    if current_frame.ticks_elapsed == current_frame.duration:
        current_frame.ticks_elapsed = 0
        self.played_frames.append(self.frames.pop(0))
        if self.is_looping and not self.frames:
            self.frames.extend(self.played_frames)
            self.played_frames.clear()
    return next_visual

reset_scene()

Resets the Scene.

Source code in terminaltexteffects/engine/animation.py
def reset_scene(self) -> None:
    """Resets the Scene."""
    for sequence in self.frames:
        sequence.ticks_elapsed = 0
        self.played_frames.append(sequence)
    self.frames.clear()
    self.frames.extend(self.played_frames)
    self.played_frames.clear()