Add an implementation for the core animations system
This commit is contained in:
parent
4da01b63b0
commit
352fe39468
4 changed files with 323 additions and 0 deletions
|
@ -80,6 +80,9 @@ target_sources_ifdef(CONFIG_ZMK_BACKLIGHT app PRIVATE src/behaviors/behavior_bac
|
|||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/battery.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/animation.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_ANIMATION app PRIVATE src/animation/color.c)
|
||||
|
||||
target_sources_ifdef(CONFIG_ZMK_SPLIT app PRIVATE src/events/split_peripheral_status_changed.c)
|
||||
add_subdirectory(src/split)
|
||||
|
||||
|
|
70
app/include/zmk/animation.h
Normal file
70
app/include/zmk/animation.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2021 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/led_strip.h>
|
||||
|
||||
struct animation_pixel_position {
|
||||
const uint8_t x;
|
||||
const uint8_t y;
|
||||
};
|
||||
|
||||
struct animation_pixel {
|
||||
const struct device *animation;
|
||||
const struct animation_pixel_position position;
|
||||
};
|
||||
|
||||
struct zmk_color_rgb {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
|
||||
struct zmk_color_hsl {
|
||||
uint16_t h;
|
||||
uint8_t s;
|
||||
uint8_t l;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts color from HSL to RGB.
|
||||
*
|
||||
* @param hsl [description]
|
||||
* @param rgb [description]
|
||||
*/
|
||||
void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb);
|
||||
|
||||
/**
|
||||
* Converts the internal RGB representation into a led_rgb struct
|
||||
* for use with led_strip drivers.
|
||||
*
|
||||
* @param rgb [description]
|
||||
* @param led [description]
|
||||
*/
|
||||
void zmk_rgb_to_led_rgb(const struct zmk_color_rgb *rgb, struct led_rgb *led);
|
||||
|
||||
/**
|
||||
* Returns true if two HSL colors are the same.
|
||||
*
|
||||
* @param a [description]
|
||||
* @param b [description]
|
||||
* @return [description]
|
||||
*/
|
||||
bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b);
|
||||
|
||||
/**
|
||||
* Perform linear interpolation between HSL values of two colors
|
||||
* at a given distance (step) and store the resulting value in the given pointer.
|
||||
*
|
||||
* @param from [description]
|
||||
* @param to [description]
|
||||
* @param result [description]
|
||||
* @param step [description]
|
||||
*/
|
||||
void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to,
|
||||
struct zmk_color_hsl *result, float step);
|
166
app/src/animation/animation.c
Normal file
166
app/src/animation/animation.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_animation
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <device.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <drivers/animation.h>
|
||||
#include <drivers/led_strip.h>
|
||||
|
||||
#include <zmk/animation.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#define PHANDLE_TO_DEVICE(node_id, prop, idx) \
|
||||
DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)),
|
||||
|
||||
#define PHANDLE_TO_CHAIN_LENGTH(node_id, prop, idx) \
|
||||
DT_PROP_BY_PHANDLE_IDX(node_id, prop, idx, chain_length),
|
||||
|
||||
#define PHANDLE_TO_PIXEL(node_id, prop, idx) \
|
||||
{ \
|
||||
.animation = DEVICE_DT_GET(DT_PHA_BY_IDX(node_id, prop, idx, animation)), \
|
||||
.position = { \
|
||||
.x = DT_PHA_BY_IDX(node_id, prop, idx, position_x), \
|
||||
.y = DT_PHA_BY_IDX(node_id, prop, idx, position_y), \
|
||||
}, \
|
||||
},
|
||||
|
||||
/**
|
||||
* LED Driver device pointers.
|
||||
*/
|
||||
static const struct device *drivers[] = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_DEVICE)
|
||||
};
|
||||
|
||||
/**
|
||||
* Size of the LED driver device pointers array.
|
||||
*/
|
||||
static const size_t drivers_size = DT_INST_PROP_LEN(0, drivers);
|
||||
|
||||
/**
|
||||
* Array containing the number of LEDs handled by each device.
|
||||
*/
|
||||
static const uint8_t pixels_per_driver[] = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, drivers, PHANDLE_TO_CHAIN_LENGTH)
|
||||
};
|
||||
|
||||
/**
|
||||
* Pointers to all active animation devices.
|
||||
*/
|
||||
static const struct device *animations[] = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, animations, PHANDLE_TO_DEVICE)
|
||||
};
|
||||
|
||||
/**
|
||||
* Size of the animation device pointers array.
|
||||
*/
|
||||
static const size_t animations_size = DT_INST_PROP_LEN(0, animations);
|
||||
|
||||
/**
|
||||
* Pixel configuration.
|
||||
*/
|
||||
static const struct animation_pixel pixels[] = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, animations, PHANDLE_TO_PIXEL)
|
||||
};
|
||||
|
||||
/**
|
||||
* Size of the pixels array.
|
||||
*/
|
||||
const size_t pixels_size = DT_INST_PROP_LEN(0, pixels);
|
||||
|
||||
/**
|
||||
* Buffer for RGB values ready to be sent to the drivers.
|
||||
*/
|
||||
struct led_rgb px_buffer[DT_INST_PROP_LEN(0, pixels)];
|
||||
|
||||
void zmk_animation_tick() {
|
||||
for (size_t i = 0; i < ZMK_ANIMATIONS_LENGTH; ++i) {
|
||||
animation_prep_next_frame(animations[i]);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < pixels_size; ++i) {
|
||||
zmk_color_rgb rgb = {
|
||||
.r = 0,
|
||||
.g = 0,
|
||||
.b = 0,
|
||||
};
|
||||
|
||||
animation_get_pixel(pixel.animation, &pixel.position, &rgb);
|
||||
|
||||
zmk_rgb_to_led_rgb(&rgb, &px_buffer[i]);
|
||||
}
|
||||
|
||||
size_t pixels_updated = 0;
|
||||
|
||||
for (size_t i = 0; i < drivers_size; ++i) {
|
||||
led_strip_update_rgb(
|
||||
drivers[i],
|
||||
&px_buffer[pixels_updated],
|
||||
pixels_per_driver[i]
|
||||
);
|
||||
|
||||
pixels_updated += pixels_per_driver;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up timer here
|
||||
|
||||
K_WORK_DEFINE(animation_work, zmk_animation_tick);
|
||||
|
||||
static void zmk_animation_tick_handler(struct k_timer *timer) {
|
||||
k_work_submit(&animation_work);
|
||||
}
|
||||
|
||||
K_TIMER_DEFINE(animation_tick, zmk_animation_tick_handler, NULL);
|
||||
|
||||
// Init
|
||||
|
||||
void zmk_animation_init(const struct device *dev) {
|
||||
// default FPS: 24, 30 or 60?
|
||||
k_timer_start(&animation_tick, K_NO_WAIT, K_MSEC(1000 / ZMK_ANIMATION_FPS));
|
||||
}
|
||||
|
||||
// Actually, all of this might be a little hard to represent inside a config file.
|
||||
// We need:
|
||||
//
|
||||
// - Each animation has its own node
|
||||
//
|
||||
// - animations[] = phandle array of animations
|
||||
//
|
||||
// - pixel.pos = x,y coordinates of each pixels, could be a byte array
|
||||
//
|
||||
// - pixel.animations = Number array, but it's per pixel and it's not fixed length per pixel!! PROBLEM!!!
|
||||
// - pixel.blending_modes = Could be a number array again. Use some macros.
|
||||
//
|
||||
// - unless we say there can only be 32 different animations and use uint32_t as a bitmask?
|
||||
// - but then blending modes suck and you can't order them
|
||||
//
|
||||
|
||||
// ALTERNATIVE:
|
||||
//
|
||||
// Eventually we don't want to be using the device tree to set these up anyway.
|
||||
// It should be possible to instantiate animations dynamically and the settings should be stored in flash.
|
||||
// Maybe it's something to look into now?
|
||||
//
|
||||
|
||||
// ALTERNATIVE no2:
|
||||
//
|
||||
// Look at behaviors.
|
||||
// It seems that pixels themselves could be 'drivers' ?
|
||||
//
|
||||
// That's probably best tbh
|
||||
|
||||
// NOTE WHEN DEFINING PIXELS:
|
||||
// See if it's maybe possible to re-use default_transform to assign pixels to keys?
|
||||
// Assuming the first batch is always the keys which is not an easy assumption to make.
|
||||
// Otherwise need it's own map
|
84
app/src/animation/color.c
Normal file
84
app/src/animation/color.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <zmk/animation.h>
|
||||
|
||||
/**
|
||||
* HSL chosen over HSV/HSB as it shares the same parameters with LCh or HSLuv.
|
||||
* The latter color spaces could be interesting to experiment with because of their
|
||||
* perceptual uniformity, but it would come at the cost of some performance.
|
||||
* Using the same parameters would make it easy to toggle any such behavior using a single config flag.
|
||||
*
|
||||
* Algorithm source: https://www.tlbx.app/color-converter
|
||||
*/
|
||||
void zmk_hsl_to_rgb(const struct zmk_color_hsl *hsl, struct zmk_color_rgb *rgb) {
|
||||
// float a = hsl->h / 60;
|
||||
uint8_t a = hsl->h / 60;
|
||||
float chroma = hsl->s * (1 - abs(2 * hsl->l - 1));
|
||||
// I think 'a' actually needs to be a float here or this doesn't make sense.
|
||||
// If uint, possible values are: (0,1), if float: (0...1)
|
||||
float x = chroma * (1 - abs(a % 2 - 1));
|
||||
float m = hsl->l - chroma / 2;
|
||||
|
||||
switch (a) {
|
||||
case 0:
|
||||
rgb->r = m + chroma;
|
||||
rgb->g = m + x;
|
||||
rgb->b = m;
|
||||
case 1:
|
||||
rgb->r = m + x;
|
||||
rgb->g = m + chroma;
|
||||
rgb->b = m;
|
||||
case 2:
|
||||
rgb->r = m;
|
||||
rgb->g = m + chroma;
|
||||
rgb->b = m + x;
|
||||
case 3:
|
||||
rgb->r = m;
|
||||
rgb->g = m + x;
|
||||
rgb->b = m + chroma;
|
||||
case 4:
|
||||
rgb->r = m + x;
|
||||
rgb->g = m;
|
||||
rgb->b = m + chroma;
|
||||
case 5:
|
||||
rgb->r = m + chroma;
|
||||
rgb->g = m;
|
||||
rgb->b = m + x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts ZMKs RGB (float) to Zephyr's led_rgb (uint8_t) format.
|
||||
*/
|
||||
void zmk_rgb_to_led_rgb(const struct zmk_color_rgb *rgb, struct led_rgb *led) {
|
||||
led->r = rgb->r * 255;
|
||||
led->g = rgb->g * 255;
|
||||
led->b = rgb->b * 255;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two HSL colors.
|
||||
*/
|
||||
bool zmk_cmp_hsl(const struct zmk_color_hsl *a, const struct zmk_color_hsl *b) {
|
||||
return a->h == b->h && a->s == b->s && a->l == b->l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interpolate between two colors using the cylindrical model (HSL).
|
||||
*/
|
||||
void zmk_interpolate_hsl(const struct zmk_color_hsl *from, const struct zmk_color_hsl *to,
|
||||
struct zmk_color_hsl *result, float step) {
|
||||
int16_t hue_delta;
|
||||
|
||||
hue_delta = from->h - to->h;
|
||||
hue_delta = hue_delta + (180 < abs(hue_delta) ? (hue_delta < 0 ? 360 : -360) : 0);
|
||||
|
||||
result->h = (uint16_t) (360 + from->h - (hue_delta * step)) % 360;
|
||||
result->s = from->s - (from->s - to->s) * step;
|
||||
result->l = from->l - (from->l - to->l) * step;
|
||||
}
|
Loading…
Add table
Reference in a new issue