Add test for steps easing function value calculation

This commit is contained in:
Kovid Goyal 2024-07-17 17:22:43 +05:30
parent e927f8da62
commit 39dfa75fe7
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
4 changed files with 53 additions and 9 deletions

View file

@ -41,6 +41,7 @@ typedef struct Animation {
#include "animation.h"
#include "state.h"
Animation*
alloc_animation(void) {
@ -178,6 +179,8 @@ apply_easing_curve(const Animation *a, double val, monotonic_t duration) {
if (!a->count) return val;
size_t idx = MIN((size_t)(val * a->count), a->count - 1);
animation_function *f = a->functions + idx;
double interval_size = 1. / a->count, interval_start = val - idx * interval_size;
val = (val - interval_start) / interval_size;
double ans = f->curve(&f->params, val, duration);
return f->y_at_start + unit_value(ans) * f->y_size;
}
@ -244,13 +247,10 @@ add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t cou
double jump_size = 1. / count, start_value = 0.;
size_t num_of_buckets = count;
switch (step) {
case EASING_STEP_START:
start_value = jump_size;
num_of_buckets--;
break;
case EASING_STEP_START: start_value = jump_size; break;
case EASING_STEP_END: break;
case EASING_STEP_NONE:
num_of_buckets--;
jump_size = 1. / (num_of_buckets - 1);
break;
case EASING_STEP_BOTH:
num_of_buckets++;
@ -264,3 +264,28 @@ add_steps_animation(Animation *a, double y_at_start, double y_at_end, size_t cou
animation_function *f = init_function(a, y_at_start, y_at_end, step_easing_curve);
f->params = p;
}
static PyObject*
test_cursor_blink_easing_function(PyObject *self UNUSED, PyObject *args) {
Animation *a = OPT(animation.cursor);
if (!animation_is_valid(a)) {
PyErr_SetString(PyExc_RuntimeError, "must set a cursor blink animation on the global options object first");
return NULL;
}
double t, duration_s = 0.5;
if (!PyArg_ParseTuple(args, "d|d", &t, &duration_s)) return NULL;
monotonic_t duration = s_double_to_monotonic_t(duration_s);
animation_function f = a->functions[0];
return PyFloat_FromDouble(f.curve(f.params, t, duration));
}
static PyMethodDef module_methods[] = {
METHODB(test_cursor_blink_easing_function, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};
bool init_animations(PyObject *module) {
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
return true;
}

View file

@ -488,6 +488,7 @@ extern bool init_child_monitor(PyObject *);
extern int init_Line(PyObject *);
extern int init_ColorProfile(PyObject *);
extern int init_Screen(PyObject *);
extern bool init_animations(PyObject*);
extern bool init_fontconfig_library(PyObject*);
extern bool init_crypto_library(PyObject*);
extern bool init_desktop(PyObject*);
@ -573,6 +574,7 @@ PyInit_fast_data_types(void) {
if (!init_loop_utils(m)) return NULL;
if (!init_crypto_library(m)) return NULL;
if (!init_systemd_module(m)) return NULL;
if (!init_animations(m)) return NULL;
CellAttrs a;
#define s(name, attr) { a.val = 0; a.attr = 1; PyModule_AddIntConstant(m, #name, shift_to_first_set_bit(a)); }

View file

@ -139,7 +139,7 @@ add_easing_function(Animation *a, PyObject *e, double y_at_start, double y_at_en
if (x) {
double *y = x + count;
for (size_t i = 0; i < count; i++) {
x[i] = D(linear_x, i); y[i] = D(y, i);
x[i] = D(linear_x, i); y[i] = D(linear_y, i);
}
add_linear_animation(a, y_at_start, y_at_end, count, x, y);
}
@ -158,9 +158,9 @@ add_easing_function(Animation *a, PyObject *e, double y_at_start, double y_at_en
static inline void
cursor_blink_interval(PyObject *src, Options *opts) {
free_animation(opts->animation.cursor);
opts->cursor_blink_interval = parse_s_double_to_monotonic_t(PyTuple_GET_ITEM(src, 0));
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 1))) {
free_animation(opts->animation.cursor);
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 1)) && (opts->animation.cursor = alloc_animation()) != NULL) {
add_easing_function(opts->animation.cursor, PyTuple_GET_ITEM(src, 1), 1, 0);
if (PyObject_IsTrue(PyTuple_GET_ITEM(src, 2))) {
add_easing_function(opts->animation.cursor, PyTuple_GET_ITEM(src, 2), 0, 1);

View file

@ -2,7 +2,7 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from kitty.fast_data_types import Color
from kitty.fast_data_types import Color, test_cursor_blink_easing_function
from kitty.options.utils import DELETE_ENV_VAR, EasingFunction
from kitty.utils import log_error
@ -155,3 +155,20 @@ class TestConfParsing(BaseTest):
cb('linear(0, 0.25, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.5, 1.0), linear_y=(0, 0.25, 1.0)))
cb('linear(0, 0.25 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.75, 1.0), linear_y=(0, 0.25, 1.0)))
cb('linear(0, 0.25 25% 75%, 1)', first=EasingFunction('linear', linear_x=(0.0, 0.25, 0.75, 1.0), linear_y=(0, 0.25, 0.25, 1.0)))
# test that easing functions give expected values
def ef(spec, tests, duration=0.5):
cfv = p('cursor_blink_interval ' + spec).cursor_blink_interval
self.set_options({'cursor_blink_interval': cfv})
for t, expected in tests.items():
actual = test_cursor_blink_easing_function(t, duration)
self.ae(expected, actual, f'Failed for {spec=} with {t=}: {expected} != {actual}')
ef('linear(0, 0.25 25% 75%, 1)', {0: 0, 0.25: 0.25, 0.3: 0.25, 0.75: 0.25, 1:1})
for spec in ('linear', 'linear(0, 1)'):
ef(spec, {0: 0, 1: 1, 0.1234: 0.1234, 0.6453: 0.6453})
ef('steps(5)', {0: 0, 0.1: 0, 0.3: 0.2, 0.9:0.8})
ef('steps(5, start)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})
ef('steps(4, jump-both)', {0: 0.2, 0.1: 0.2, 0.3: 0.4, 0.9:1})
ef('steps(6, jump-none)', {0: 0, 0.1: 0.0, 0.3: 0.2, 0.9:1})