---
name: tween_curves
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "float fn::tween::apply(fn::tween::Ease e, float t) + ~16 named easing functions (linear, in_quad, out_quad, ...)"
description: "Set de funciones de easing puras (Penner) para animaciones e interpolaciones: linear, quad, cubic, expo, elastic, bounce, en variantes in/out/inOut. Header-mostly: el compilador inlinea cada curva en el sitio de llamada. Sin estado, sin I/O."
tags: [animation, easing, tween, penner, math, header-only]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
tested: true
tests:
  - "linear is identity"
  - "quadratic boundary conditions"
  - "cubic boundary conditions"
  - "exponential boundary conditions"
  - "in_quad < out_quad in mid-range"
  - "monotonic curves are monotonic"
  - "elastic and bounce hit endpoints exactly"
  - "apply dispatch matches direct calls"
  - "name() returns non-null for every Ease"
test_file_path: "cpp/tests/test_tween_curves.cpp"
file_path: "cpp/functions/core/tween_curves.cpp"
framework: ""
params:
  - name: e
    desc: "Curva a aplicar (enum Ease): Linear, InQuad, OutQuad, InOutQuad, InCubic, OutCubic, InOutCubic, InExpo, OutExpo, InOutExpo, InElastic, OutElastic, InOutElastic, InBounce, OutBounce, InOutBounce"
  - name: t
    desc: "Progreso normalizado en [0,1]. Para t<0 o t>1 algunas curvas extrapolan razonablemente, otras saturan."
output: "f(t) — valor de la curva en t. Para curvas no oscilantes f(0)=0 y f(1)=1. Para elastic/bounce f(0)=0 y f(1)=1 pero f puede salir del rango [0,1] en valores intermedios."
notes: "scaffolding/demo en primitives_gallery"
---

# tween_curves

Funciones de easing al estilo Penner. Permiten convertir un progreso lineal `t in [0,1]` en una curva temporal con feel concreto (acelerar, frenar, rebotar, oscilar). Son el ladrillo basico de toda animacion: timeline interpolation, transiciones UI, smooth lerp de uniforms, ramps de color.

## API

```cpp
namespace fn::tween {

enum class Ease {
    Linear,
    InQuad, OutQuad, InOutQuad,
    InCubic, OutCubic, InOutCubic,
    InExpo, OutExpo, InOutExpo,
    InElastic, OutElastic, InOutElastic,
    InBounce, OutBounce, InOutBounce,
};

// Funciones inline named (todas (float)->float, t en [0,1]):
inline float linear(float t);
inline float in_quad(float t);   inline float out_quad(float t);   inline float in_out_quad(float t);
inline float in_cubic(float t);  inline float out_cubic(float t);  inline float in_out_cubic(float t);
inline float in_expo(float t);   inline float out_expo(float t);   inline float in_out_expo(float t);
inline float in_elastic(float t); inline float out_elastic(float t); inline float in_out_elastic(float t);
inline float in_bounce(float t); inline float out_bounce(float t); inline float in_out_bounce(float t);

// Dispatch dinamico por enum (util para data-driven: Keyframe.ease, dropdown UI)
float apply(Ease e, float t);
const char* name(Ease e); // "Linear", "InQuad", ...

constexpr int ease_count = 16;

} // namespace fn::tween
```

## Ejemplo

```cpp
#include "core/tween_curves.h"

// Lerp con easing entre dos colores
float t = elapsed_ms / duration_ms;
float k = fn::tween::out_cubic(t);
ImVec4 c = lerp(c0, c1, k);

// Data-driven (timeline keyframe)
fn::tween::Ease ease = key.ease;
float k = fn::tween::apply(ease, t_normalized);

// Dropdown ImGui con todas las curvas
static int idx = 0;
const char* labels[fn::tween::ease_count];
for (int i = 0; i < fn::tween::ease_count; i++)
    labels[i] = fn::tween::name((fn::tween::Ease)i);
ImGui::Combo("Ease", &idx, labels, fn::tween::ease_count);
```

## Valores conocidos (smoke tests)

| Curva       | t=0   | t=0.5  | t=1   |
|-------------|-------|--------|-------|
| Linear      | 0.000 | 0.500  | 1.000 |
| InQuad      | 0.000 | 0.250  | 1.000 |
| OutQuad     | 0.000 | 0.750  | 1.000 |
| InOutQuad   | 0.000 | 0.500  | 1.000 |
| InCubic     | 0.000 | 0.125  | 1.000 |
| OutCubic    | 0.000 | 0.875  | 1.000 |
| InOutCubic  | 0.000 | 0.500  | 1.000 |
| InExpo      | 0.000 | ~0.031 | 1.000 |
| OutExpo     | 0.000 | ~0.969 | 1.000 |
| InOutExpo   | 0.000 | 0.500  | 1.000 |
| OutBounce   | 0.000 | ~0.766 | 1.000 |

Para todas las curvas: `f(0) == 0` y `f(1) == 1`. Las curvas elastic/bounce pueden salir de [0,1] en valores intermedios — no es un bug, es el overshoot deseado.

## Decisiones

- **Header-mostly**: las 16 funciones son `inline` en `.h` para que el compilador inline llamadas en hot paths (animaciones por frame, miles de muestras por curva en plots).
- **`apply(enum, t)` en .cpp**: para no forzar `<switch>` enorme en headers; util para casos data-driven.
- **Sin clamp interior**: el caller decide si saturar t. Esto permite efectos de overshoot deliberado.
- **Sin templates**: los easings se usan masivamente con `float`; un `T` genérico complicaría el header sin beneficio.

## Referencias

- easings.net (visualizaciones interactivas y formulas)
- Robert Penner — *Motion, Tweening, and Easing* (libro original)
