fix(animation): ColorTransition epsilon check ANTES del lerp

El check anterior estaba DESPUÉS del lerpU8, causando loop infinito:
- lerpU8 trunca incrementos <1, current no cambia
- dr = |current - target| sigue siendo > 1 (ej: 10)
- return true → pide más frames
- Repeat forever

Nuevo algoritmo:
1. Calcular diferencia PRIMERO
2. Si diferencia <= 2 en todos los canales → snap directo
3. Solo hacer lerp si diferencia > 2

Umbral aumentado de 1 a 2 para mayor margen de seguridad.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
R.Eugenio 2026-01-03 14:30:11 +01:00
parent 908815b585
commit d92ce07bb3

View file

@ -719,13 +719,22 @@ pub const ColorTransition = struct {
return true; return true;
} }
// Already at target? // EPSILON CHECK PRIMERO: Si estamos "suficientemente cerca", snap directo
if (self.current.r == target.r and // Esto evita el problema de lerpU8 truncando incrementos <1
self.current.g == target.g and const dr = @abs(@as(i16, self.current.r) - @as(i16, target.r));
self.current.b == target.b and const dg = @abs(@as(i16, self.current.g) - @as(i16, target.g));
self.current.a == target.a) const db = @abs(@as(i16, self.current.b) - @as(i16, target.b));
const da = @abs(@as(i16, self.current.a) - @as(i16, target.a));
// Umbral pequeño (<=2) para garantizar convergencia sin saltos visibles
if (dr <= 2 and dg <= 2 and db <= 2 and da <= 2) {
if (self.current.r != target.r or self.current.g != target.g or
self.current.b != target.b or self.current.a != target.a)
{ {
return false; self.current = target; // Snap final
return true; // Un último frame para el snap
}
return false; // Ya en target, animación terminada
} }
// Calculate interpolation factor (chase towards target) // Calculate interpolation factor (chase towards target)
@ -739,19 +748,6 @@ pub const ColorTransition = struct {
.a = lerpU8(self.current.a, target.a, t), .a = lerpU8(self.current.a, target.a, t),
}; };
// EPSILON FIX: Forzar convergencia cuando estamos "suficientemente cerca"
// Sin esto, lerpU8 trunca incrementos <1 y la animación nunca termina.
// Umbral de 1 es imperceptible al ojo pero garantiza convergencia.
const dr = @abs(@as(i16, self.current.r) - @as(i16, target.r));
const dg = @abs(@as(i16, self.current.g) - @as(i16, target.g));
const db = @abs(@as(i16, self.current.b) - @as(i16, target.b));
const da = @abs(@as(i16, self.current.a) - @as(i16, target.a));
if (dr <= 1 and dg <= 1 and db <= 1 and da <= 1) {
self.current = target; // Snap al target
return false; // Animación terminada
}
return true; return true;
} }