Skip to content

Instantly share code, notes, and snippets.

@thacuber2a03
Last active October 30, 2024 23:23
Show Gist options
  • Save thacuber2a03/0e9868c2f520064e2b56c9f513d8026b to your computer and use it in GitHub Desktop.
Save thacuber2a03/0e9868c2f520064e2b56c9f513d8026b to your computer and use it in GitHub Desktop.
a header-only tweening library for C compatible with C89
#ifndef EASEL_H
#define EASEL_H
/*
** easel.h -- a tweening library compatible with ANSI/C89
** by @thacuber2a03, public domain, or CC0 where unapplicable
** release 30-10-2024 (dd-mm-yyyy)
*/
#include <string.h>
#define EASEL_VERSION "0.1.0"
#ifdef EASEL_STATIC
#define EASEL_IMPLEMENTATION
#endif
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define ESL_INLINE__ inline
#else
#define ESL_INLINE__
#endif
#ifdef EASEL_STATIC
#define ESLAPI static ESL_INLINE__
#else
#define ESLAPI extern ESL_INLINE__
#endif
/*********************************************************************************/
#define esl_util_lerp(a, b, t) ((a) + ((b) - (a)) * (t))
#define esl_util_min(a, b) ((a) < (b) ? (a) : (b))
#define esl_util_max(a, b) ((a) > (b) ? (a) : (b))
#define esl_util_clamp(v, a, b) (esl_util_max(a, esl_util_min(v, b)))
/*********************************************************************************/
typedef float esl_real;
typedef esl_real (*esl_ease_func)(esl_real t);
typedef struct {
esl_real progress;
esl_ease_func ease;
int finished;
} esl_tween_base;
#define esl_tween_t(T) \
struct { \
esl_tween_base base; \
T start, end, *value; \
void (*tween)(T *v, T *a, T *b, esl_real t); \
}
typedef void (*esl_typeerased_tween__)(void* v, void* a, void* b, esl_real t);
#define esl_unpack_tween__(t) \
&(t)->base, &(t)->start, &(t)->end, (t)->value, (esl_typeerased_tween__)((t)->tween)
#define esl_supported_prims__(X) \
X(double) X(float) X(long long) X(long) X(int) X(short) X(char)
#define esl_init(t) (memset(t, 0, sizeof *(t)))
#define esl_ease(t) ((t)->base.ease)
#define esl_progress(t) ((t)->base.progress)
#define esl_finished(t) ((t)->base.finished)
/*********************************************************************************/
#define esl_register_prim__(T) \
typedef esl_tween_t(T) esl_##T##_tween_t; \
ESLAPI void esl_##T##_tween_func(T *v, T* a, T* b, esl_real t); \
ESLAPI esl_##T##_tween_t esl_##T##_tween(T *value);
esl_supported_prims__(esl_register_prim__)
#undef esl_register_prim__
/*********************************************************************************/
#define esl_register_simple_ease__(n, e) ESLAPI esl_real esl_ease_##n(esl_real t) { return e; }
esl_register_simple_ease__(linear, t )
esl_register_simple_ease__(inQuad, t*t )
esl_register_simple_ease__(outQuad, 2*t - t*t )
esl_register_simple_ease__(inOutQuad, t*t*(3-2*t) )
#define esl_ease_smoothstep esl_ease_inOutQuad
esl_register_simple_ease__(inCubic, t*t*t )
esl_register_simple_ease__(outCubic, t*(t*(t-3)+3))
#undef esl_register_simple_ease__
/*********************************************************************************/
#define esl_func_params__ esl_tween_base* t, void *start, void *end, void *value, esl_typeerased_tween__ tween
#define esl_update(t, dt) (esl_set_(esl_unpack_tween__(t), (t)->base.progress + (dt)))
#define esl_set(t, time) (esl_set_(esl_unpack_tween__(t), time))
ESLAPI void esl_set_(esl_func_params__, esl_real time);
#endif
#ifdef EASEL_IMPLEMENTATION
#define esl_define_prim__(T) \
ESLAPI void esl_##T##_tween_func(T *v, T* a, T* b, esl_real t) { \
*v = esl_util_lerp(*a, *b, t); \
} \
\
ESLAPI esl_##T##_tween_t esl_##T##_tween(T *value) { \
esl_##T##_tween_t t; \
esl_init(&t); \
t.value = value; \
t.tween = esl_##T##_tween_func; \
return t; \
}
esl_supported_prims__(esl_define_prim__)
#undef esl_define_prim__
ESLAPI void esl_set_(esl_func_params__, esl_real time)
{
t->progress = esl_util_clamp(time, 0, 1);
if (t->progress == 1) t->finished = 1;
tween(value, start, end, t->ease(t->progress));
}
#endif
#undef esl_supported_prims__
#undef esl_func_params__
#undef ESL_INLINE__
#undef ESLAPI
@thacuber2a03
Copy link
Author

thacuber2a03 commented Oct 30, 2024

usage

include this file somewhere in your project.
define EASEL_IMPLEMENTATION before including it to place the function implementations in that file.
define EASEL_STATIC to implement all the functions statically in the current file.

api

esl_tween_t(T)

the macro for tween types.
there are 5 predefined versions of this macro, for double, float, long long, long, int, short and char, respectively.

each definition also comes with a function, esl_T_tween, and an easing typedef, esl_T_tween_func,
where T stands for one of the types listed above.

esl_update(t, dt)

step tween t by dt.

esl_set(t, progress)

set the progress of a tween t. progress must be a number from 0 to 1.

esl_real

the value standing for a real number type in the library.

esl_ease_func

the typedef for an easing function.

esl_tween_base

the base struct all tweening structs extend.
esl_ease, esl_progress and esl_finished are shorthands for accessing each of the properties in this struct for individual tweens.

esl_ease_F(esl_real t)

predefined easing formulas for some common eases.

F can be one of:

  • linear
  • inQuad
  • outQuad
  • inOutQuad
  • smoothstep (alias for inOutQuad)
  • inCubic
  • outCubic

EASEL_VERSION

the current version of the library, as a semver string.

utility macros

name
esl_util_lerp(a, b, t)
esl_util_min(a, b)
esl_util_max(a, b)
esl_util_clamp(v, a, b)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment