Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Creates optimized animated GIFs for Slack emoji (128x128) or messages (480x480) using Python PIL with polished drawing primitives.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
core/easing.py
1#!/usr/bin/env python32"""3Easing Functions - Timing functions for smooth animations.45Provides various easing functions for natural motion and timing.6All functions take a value t (0.0 to 1.0) and return eased value (0.0 to 1.0).7"""89import math101112def linear(t: float) -> float:13"""Linear interpolation (no easing)."""14return t151617def ease_in_quad(t: float) -> float:18"""Quadratic ease-in (slow start, accelerating)."""19return t * t202122def ease_out_quad(t: float) -> float:23"""Quadratic ease-out (fast start, decelerating)."""24return t * (2 - t)252627def ease_in_out_quad(t: float) -> float:28"""Quadratic ease-in-out (slow start and end)."""29if t < 0.5:30return 2 * t * t31return -1 + (4 - 2 * t) * t323334def ease_in_cubic(t: float) -> float:35"""Cubic ease-in (slow start)."""36return t * t * t373839def ease_out_cubic(t: float) -> float:40"""Cubic ease-out (fast start)."""41return (t - 1) * (t - 1) * (t - 1) + 1424344def ease_in_out_cubic(t: float) -> float:45"""Cubic ease-in-out."""46if t < 0.5:47return 4 * t * t * t48return (t - 1) * (2 * t - 2) * (2 * t - 2) + 1495051def ease_in_bounce(t: float) -> float:52"""Bounce ease-in (bouncy start)."""53return 1 - ease_out_bounce(1 - t)545556def ease_out_bounce(t: float) -> float:57"""Bounce ease-out (bouncy end)."""58if t < 1 / 2.75:59return 7.5625 * t * t60elif t < 2 / 2.75:61t -= 1.5 / 2.7562return 7.5625 * t * t + 0.7563elif t < 2.5 / 2.75:64t -= 2.25 / 2.7565return 7.5625 * t * t + 0.937566else:67t -= 2.625 / 2.7568return 7.5625 * t * t + 0.984375697071def ease_in_out_bounce(t: float) -> float:72"""Bounce ease-in-out."""73if t < 0.5:74return ease_in_bounce(t * 2) * 0.575return ease_out_bounce(t * 2 - 1) * 0.5 + 0.5767778def ease_in_elastic(t: float) -> float:79"""Elastic ease-in (spring effect)."""80if t == 0 or t == 1:81return t82return -math.pow(2, 10 * (t - 1)) * math.sin((t - 1.1) * 5 * math.pi)838485def ease_out_elastic(t: float) -> float:86"""Elastic ease-out (spring effect)."""87if t == 0 or t == 1:88return t89return math.pow(2, -10 * t) * math.sin((t - 0.1) * 5 * math.pi) + 1909192def ease_in_out_elastic(t: float) -> float:93"""Elastic ease-in-out."""94if t == 0 or t == 1:95return t96t = t * 2 - 197if t < 0:98return -0.5 * math.pow(2, 10 * t) * math.sin((t - 0.1) * 5 * math.pi)99return math.pow(2, -10 * t) * math.sin((t - 0.1) * 5 * math.pi) * 0.5 + 1100101102# Convenience mapping103EASING_FUNCTIONS = {104"linear": linear,105"ease_in": ease_in_quad,106"ease_out": ease_out_quad,107"ease_in_out": ease_in_out_quad,108"bounce_in": ease_in_bounce,109"bounce_out": ease_out_bounce,110"bounce": ease_in_out_bounce,111"elastic_in": ease_in_elastic,112"elastic_out": ease_out_elastic,113"elastic": ease_in_out_elastic,114}115116117def get_easing(name: str = "linear"):118"""Get easing function by name."""119return EASING_FUNCTIONS.get(name, linear)120121122def interpolate(start: float, end: float, t: float, easing: str = "linear") -> float:123"""124Interpolate between two values with easing.125126Args:127start: Start value128end: End value129t: Progress from 0.0 to 1.0130easing: Name of easing function131132Returns:133Interpolated value134"""135ease_func = get_easing(easing)136eased_t = ease_func(t)137return start + (end - start) * eased_t138139140def ease_back_in(t: float) -> float:141"""Back ease-in (slight overshoot backward before forward motion)."""142c1 = 1.70158143c3 = c1 + 1144return c3 * t * t * t - c1 * t * t145146147def ease_back_out(t: float) -> float:148"""Back ease-out (overshoot forward then settle back)."""149c1 = 1.70158150c3 = c1 + 1151return 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2)152153154def ease_back_in_out(t: float) -> float:155"""Back ease-in-out (overshoot at both ends)."""156c1 = 1.70158157c2 = c1 * 1.525158if t < 0.5:159return (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2160return (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2161162163def apply_squash_stretch(164base_scale: tuple[float, float], intensity: float, direction: str = "vertical"165) -> tuple[float, float]:166"""167Calculate squash and stretch scales for more dynamic animation.168169Args:170base_scale: (width_scale, height_scale) base scales171intensity: Squash/stretch intensity (0.0-1.0)172direction: 'vertical', 'horizontal', or 'both'173174Returns:175(width_scale, height_scale) with squash/stretch applied176"""177width_scale, height_scale = base_scale178179if direction == "vertical":180# Compress vertically, expand horizontally (preserve volume)181height_scale *= 1 - intensity * 0.5182width_scale *= 1 + intensity * 0.5183elif direction == "horizontal":184# Compress horizontally, expand vertically185width_scale *= 1 - intensity * 0.5186height_scale *= 1 + intensity * 0.5187elif direction == "both":188# General squash (both dimensions)189width_scale *= 1 - intensity * 0.3190height_scale *= 1 - intensity * 0.3191192return (width_scale, height_scale)193194195def calculate_arc_motion(196start: tuple[float, float], end: tuple[float, float], height: float, t: float197) -> tuple[float, float]:198"""199Calculate position along a parabolic arc (natural motion path).200201Args:202start: (x, y) starting position203end: (x, y) ending position204height: Arc height at midpoint (positive = upward)205t: Progress (0.0-1.0)206207Returns:208(x, y) position along arc209"""210x1, y1 = start211x2, y2 = end212213# Linear interpolation for x214x = x1 + (x2 - x1) * t215216# Parabolic interpolation for y217# y = start + progress * (end - start) + arc_offset218# Arc offset peaks at t=0.5219arc_offset = 4 * height * t * (1 - t)220y = y1 + (y2 - y1) * t - arc_offset221222return (x, y)223224225# Add new easing functions to the convenience mapping226EASING_FUNCTIONS.update(227{228"back_in": ease_back_in,229"back_out": ease_back_out,230"back_in_out": ease_back_in_out,231"anticipate": ease_back_in, # Alias232"overshoot": ease_back_out, # Alias233}234)235