---
name: bezier_editor
kind: component
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "bool fn::bezier_editor(const char* id, fn::BezierCurve& curve, ImVec2 size = {200,200}, bool lock_endpoints = true) + float fn::bezier_eval(const BezierCurve&, float t)"
description: "Editor visual de una curva Bezier cubica (4 puntos de control). Permite diseñar easing curves custom arrastrando p1 y p2 (p0 y p3 fijos en (0,0) y (1,1)). Evaluacion via De Casteljau + sampling. Render en canvas ImGui usando tokens (primary, surface, border)."
tags: [imgui, bezier, animation, easing, editor, canvas, pendiente-usar]
uses_functions:
  - tokens_cpp_core
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/bezier_editor.cpp"
framework: imgui
params:
  - name: id
    desc: "ID ImGui unico (formato '##nombre' tipico para no mostrar texto)"
  - name: curve
    desc: "Estado de la curva (struct BezierCurve con p0..p3 en [0,1]x[0,1]). Modificada por el widget."
  - name: size
    desc: "Tamaño del canvas en pixels. Recomendado >= 180x180 para precision de drag."
  - name: lock_endpoints
    desc: "Si true (default) p0 y p3 quedan fijos en (0,0) y (1,1) — uso como easing curve. Si false, p0/p3 son draggable."
output: "true en el frame en que el usuario arrastro algun punto de control"
---

# bezier_editor

Editor inline en canvas ImGui para diseñar curvas Bezier cubicas. La aplicacion tipica es disenar **easing curves custom** que no encajan con las preset de `tween_curves` (Penner). Tambien sirve para definir ramps de color, perfiles de velocidad, etc.

## Estado y evaluacion (puro)

```cpp
struct fn::BezierCurve {
    ImVec2 p0 {0,0};
    ImVec2 p1 {0.25f, 0.0f};
    ImVec2 p2 {0.75f, 1.0f};
    ImVec2 p3 {1,1};
};

ImVec2 fn::bezier_point(const BezierCurve&, float u);  // De Casteljau, devuelve (x,y) en u
float  fn::bezier_eval (const BezierCurve&, float t);  // y at x=t (sampling 64 + interp lineal)
```

`bezier_eval` asume monotonia en X (caso tipico de easing). Para curvas con overshoot horizontal el resultado puede saltar — no es un bug, es una limitacion del modelo de easing-curve.

## Render

```cpp
static fn::BezierCurve curve;  // identidad lineal por defecto
if (fn::bezier_editor("##my_ease", curve, ImVec2(220, 220))) {
    // El usuario movio un punto este frame; recomputar lo que dependa.
}
float y = fn::bezier_eval(curve, t);
```

## Visuales

- Fondo `bg`, borde `border`, grid 4x4 con `border` (subtle).
- Diagonal de referencia (linear) con `text_dim`.
- Lineas tangentes p0->p1 y p3->p2 con `text_muted`.
- Curva con `primary` y grosor 2.
- Handles: circulos `primary` para draggable, `text_dim` para los lockeados.

## Interaccion

- Drag de p1 / p2 con boton izquierdo del raton.
- Si `lock_endpoints=false`, p0 y p3 tambien arrastrables.
- p1.x y p2.x se clampan a [0,1]; las Y son libres (permiten overshoot deliberado).
- Tamaño minimo recomendado **180x180**: por debajo el drag se vuelve impreciso.

## Tests conocidos

- Curva identidad `{(0,0),(0.33,0.33),(0.66,0.66),(1,1)}` → `bezier_eval(c, 0.5) ~= 0.5` (tolerancia ~0.01 por sampling).
- `bezier_eval(c, 0.0) == 0.0` y `bezier_eval(c, 1.0) == 1.0` para cualquier curva con `lock_endpoints=true`.

## Decisiones

- **Sampling 64 + lerp** para `bezier_eval` en vez de inversion analitica (Cardano): mas simple, rapido, suficiente para easing.
- **Sin guardar curva en estado interno**: el caller posee `BezierCurve` (igual que el resto de primitivos del registry).
- **Endpoints lockeados por defecto**: el caso 99% de uso (easing) ya define p0=(0,0), p3=(1,1).

## Notas

- No hay snap a grid en el MVP. Si hace falta, exponer un parametro adicional o usar Shift+drag.
- El editor consume todo el ancho de la celda padre si `size.x = 0` (auto).
