from typing import Optional from libs.utils import get_current_ms class Animation: def __init__(self, current_ms: float, duration: float, type: str): self.type = type self.start_ms = current_ms self.attribute = 0 self.duration = duration self.params = dict() self.is_finished = False def update(self, current_ms: float): if self.type == 'move': self.move(current_ms, self.duration, self.params['total_distance'], self.params['start_position'], delay=self.params.get('delay', 0.0), ease_in=self.params.get('ease_in', None), ease_out=self.params.get('ease_out', None)) elif self.type == 'texture_change': self.texture_change(current_ms, self.duration, self.params['textures']) elif self.type == 'text_stretch': self.text_stretch(current_ms, self.duration) elif self.type == 'texture_resize': self.texture_resize(current_ms, self.duration, initial_size=self.params.get('initial_size', 1.0), final_size=self.params.get('final_size', 1.0), delay=self.params.get('delay', 0.0)) if self.params.get('reverse', None) is not None and current_ms - self.start_ms >= self.duration + self.params.get('delay', 0.0): self.texture_resize(current_ms, self.duration, final_size=self.params.get('initial_size', 1.0), initial_size=self.params.get('final_size', 1.0), delay=self.params.get('delay', 0.0) + self.duration) def _ease_out_progress(self, progress: float, ease: str | None) -> float: if ease == 'quadratic': return progress * (2 - progress) elif ease == 'cubic': return 1 - pow(1 - progress, 3) elif ease == 'exponential': return 1 - pow(2, -10 * progress) else: return progress def _ease_in_progress(self, progress: float, ease: str | None) -> float: if ease == 'quadratic': return progress * progress elif ease == 'cubic': return progress * progress * progress elif ease == 'exponential': return pow(2, 10 * (progress - 1)) else: return progress def move(self, current_ms: float, duration: float, total_distance: float, start_position: float, delay: float, ease_in: str | None, ease_out: str | None) -> None: elapsed_time = current_ms - self.start_ms if elapsed_time < delay: self.attribute = start_position elapsed_time -= delay if elapsed_time <= duration: if ease_in is not None: progress = self._ease_in_progress(elapsed_time / duration, ease_in) elif ease_out is not None: progress = self._ease_out_progress(elapsed_time / duration, ease_out) else: progress = elapsed_time / duration self.attribute = start_position + (total_distance * progress) else: self.attribute = start_position + total_distance self.is_finished = True def texture_change(self, current_ms: float, duration: float, textures: list[tuple[float, float, int]]) -> None: elapsed_time = current_ms - self.start_ms if elapsed_time <= duration: for start, end, index in textures: if start < elapsed_time <= end: self.attribute = index else: self.is_finished = True def text_stretch(self, current_ms: float, duration: float): elapsed_time = current_ms - self.start_ms if elapsed_time <= duration: self.attribute = 2 + 5 * (elapsed_time // 25) elif elapsed_time <= duration + 116: frame_time = (elapsed_time - duration) // 16.57 self.attribute = 2 + 10 - (2 * (frame_time + 1)) else: self.attribute = 0 self.is_finished = True def texture_resize(self, current_ms: float, duration: float, initial_size: float, final_size: float, delay: float): elapsed_time = current_ms - self.start_ms if elapsed_time < delay: self.attribute = initial_size elapsed_time -= delay if elapsed_time >= duration: self.attribute = final_size self.is_finished = True elif elapsed_time < duration: progress = elapsed_time / duration self.attribute = initial_size + ((final_size - initial_size) * progress) else: self.attribute = final_size self.is_finished = True class BaseAnimation(): def __init__(self, duration: float, delay: float = 0.0): """ Initialize a base animation. Args: duration: Length of the animation in milliseconds delay: Time to wait before starting the animation reverse_delay: If provided, animation will play in reverse after this delay """ self.duration = duration self.delay = delay self.start_ms = get_current_ms() self.is_finished = False self.attribute = 0 def update(self, current_time_ms: float) -> None: """Update the animation based on the current time.""" pass def _ease_in(self, progress: float, ease_type: str) -> float: if ease_type == "quadratic": return progress * progress elif ease_type == "cubic": return progress * progress * progress elif ease_type == "exponential": return 0 if progress == 0 else pow(2, 10 * (progress - 1)) return progress def _ease_out(self, progress: float, ease_type: str) -> float: if ease_type == "quadratic": return progress * (2 - progress) elif ease_type == "cubic": return 1 - pow(1 - progress, 3) elif ease_type == "exponential": return 1 if progress == 1 else 1 - pow(2, -10 * progress) return progress def _apply_easing(self, progress: float, ease_in: Optional[str] = None, ease_out: Optional[str] = None) -> float: if ease_in: return self._ease_in(progress, ease_in) elif ease_out: return self._ease_out(progress, ease_out) return progress class FadeAnimation(BaseAnimation): def __init__(self, duration: float, initial_opacity: float = 1.0, final_opacity: float = 0.0, delay: float = 0.0, ease_in: Optional[str] = None, ease_out: Optional[str] = None, reverse_delay: Optional[float] = None): super().__init__(duration, delay) self.initial_opacity = initial_opacity self.final_opacity = final_opacity self.ease_in = ease_in self.ease_out = ease_out self.reverse_delay = reverse_delay def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms if elapsed_time <= self.delay: self.attribute = self.initial_opacity elif elapsed_time >= self.delay + self.duration: self.attribute = self.final_opacity if self.reverse_delay is not None: self.start_ms = current_time_ms self.delay = self.reverse_delay self.initial_opacity, self.final_opacity = self.final_opacity, self.initial_opacity self.reverse_delay = None else: self.is_finished = True else: animation_time = elapsed_time - self.delay progress = animation_time / self.duration progress = max(0.0, min(1.0, progress)) progress = self._apply_easing(progress, self.ease_in, self.ease_out) self.attribute = self.initial_opacity + progress * (self.final_opacity - self.initial_opacity) class MoveAnimation(BaseAnimation): def __init__(self, duration: float, total_distance: int = 0, start_position: int = 0, delay: float = 0.0, ease_in: Optional[str] = None, ease_out: Optional[str] = None): super().__init__(duration, delay) self.total_distance = total_distance self.start_position = start_position self.ease_in = ease_in self.ease_out = ease_out def update(self, current_time_ms: float): elapsed_time = current_time_ms - self.start_ms if elapsed_time < self.delay: self.attribute = self.start_position elif elapsed_time >= self.delay + self.duration: self.attribute = self.start_position + self.total_distance self.is_finished = True else: progress = (elapsed_time - self.delay) / self.duration progress = self._apply_easing(progress, self.ease_in, self.ease_out) self.attribute = self.start_position + (self.total_distance * progress) class Animation2: """Factory for creating different types of animations.""" @staticmethod def create_fade(duration: float, **kwargs) -> FadeAnimation: """Create a fade animation.""" return FadeAnimation(duration, **kwargs) @staticmethod def create_move(duration: float, **kwargs) -> MoveAnimation: """Create a movement animation.""" return MoveAnimation(duration, **kwargs) ''' @staticmethod def create_texture_change(duration: float, **kwargs) -> TextureChangeAnimation: """Create a texture change animation.""" return TextureChangeAnimation(duration, **kwargs) @staticmethod def create_text_stretch(duration: float) -> TextStretchAnimation: """Create a text stretch animation.""" return TextStretchAnimation(duration) @staticmethod def create_texture_resize(duration: float, **kwargs) -> TextureResizeAnimation: """Create a texture resize animation.""" return TextureResizeAnimation(duration, **kwargs) '''