1#!/usr/bin/env python3
  2"""
  3Shake Animation Template - Creates shaking/vibrating motion.
  4
  5Use this for impact effects, emphasis, or nervous/excited reactions.
  6"""
  7
  8import sys
  9import math
 10from pathlib import Path
 11
 12sys.path.append(str(Path(__file__).parent.parent))
 13
 14from core.gif_builder import GIFBuilder
 15from core.frame_composer import create_blank_frame, draw_circle, draw_emoji, draw_text
 16from core.easing import ease_out_quad
 17
 18
 19def create_shake_animation(
 20    object_type: str = 'emoji',
 21    object_data: dict = None,
 22    num_frames: int = 20,
 23    shake_intensity: int = 15,
 24    center_x: int = 240,
 25    center_y: int = 240,
 26    direction: str = 'horizontal',  # 'horizontal', 'vertical', or 'both'
 27    frame_width: int = 480,
 28    frame_height: int = 480,
 29    bg_color: tuple[int, int, int] = (255, 255, 255)
 30) -> list:
 31    """
 32    Create frames for a shaking animation.
 33
 34    Args:
 35        object_type: 'circle', 'emoji', 'text', or 'custom'
 36        object_data: Data for the object
 37        num_frames: Number of frames
 38        shake_intensity: Maximum shake displacement in pixels
 39        center_x: Center X position
 40        center_y: Center Y position
 41        direction: 'horizontal', 'vertical', or 'both'
 42        frame_width: Frame width
 43        frame_height: Frame height
 44        bg_color: Background color
 45
 46    Returns:
 47        List of frames
 48    """
 49    frames = []
 50
 51    # Default object data
 52    if object_data is None:
 53        if object_type == 'emoji':
 54            object_data = {'emoji': '😱', 'size': 80}
 55        elif object_type == 'text':
 56            object_data = {'text': 'SHAKE!', 'font_size': 50, 'color': (255, 0, 0)}
 57
 58    for i in range(num_frames):
 59        frame = create_blank_frame(frame_width, frame_height, bg_color)
 60
 61        # Calculate progress
 62        t = i / (num_frames - 1) if num_frames > 1 else 0
 63
 64        # Decay shake intensity over time
 65        intensity = shake_intensity * (1 - ease_out_quad(t))
 66
 67        # Calculate shake offset using sine wave for smooth oscillation
 68        freq = 3  # Oscillation frequency
 69        offset_x = 0
 70        offset_y = 0
 71
 72        if direction in ['horizontal', 'both']:
 73            offset_x = int(math.sin(t * freq * 2 * math.pi) * intensity)
 74
 75        if direction in ['vertical', 'both']:
 76            offset_y = int(math.cos(t * freq * 2 * math.pi) * intensity)
 77
 78        # Apply offset
 79        x = center_x + offset_x
 80        y = center_y + offset_y
 81
 82        # Draw object
 83        if object_type == 'emoji':
 84            draw_emoji(
 85                frame,
 86                emoji=object_data['emoji'],
 87                position=(x - object_data['size'] // 2, y - object_data['size'] // 2),
 88                size=object_data['size']
 89            )
 90        elif object_type == 'text':
 91            draw_text(
 92                frame,
 93                text=object_data['text'],
 94                position=(x, y),
 95                font_size=object_data['font_size'],
 96                color=object_data['color'],
 97                centered=True
 98            )
 99        elif object_type == 'circle':
100            draw_circle(
101                frame,
102                center=(x, y),
103                radius=object_data.get('radius', 30),
104                fill_color=object_data.get('color', (100, 100, 255))
105            )
106
107        frames.append(frame)
108
109    return frames
110
111
112# Example usage
113if __name__ == '__main__':
114    print("Creating shake GIF...")
115
116    builder = GIFBuilder(width=480, height=480, fps=24)
117
118    frames = create_shake_animation(
119        object_type='emoji',
120        object_data={'emoji': '😱', 'size': 100},
121        num_frames=30,
122        shake_intensity=20,
123        direction='both'
124    )
125
126    builder.add_frames(frames)
127    builder.save('shake_test.gif', num_colors=128)