From 9e06823fedd36c568a31df43f5bcc419cd0a125c Mon Sep 17 00:00:00 2001 From: Constantin Gierczak--Galle Date: Sat, 9 Dec 2023 17:49:51 +0100 Subject: [PATCH] Add procedure to blend a list of colors to a 16-length list for LED bars --- pyjecteur/common.py | 87 +++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 1 + 2 files changed, 88 insertions(+) create mode 100644 pyjecteur/common.py diff --git a/pyjecteur/common.py b/pyjecteur/common.py new file mode 100644 index 0000000..3a989bd --- /dev/null +++ b/pyjecteur/common.py @@ -0,0 +1,87 @@ +import logging +from colour import Color +from typing import List, Callable, Optional, Union, Tuple +from enum import Enum + +LedBarLen = 16 + +def remove_duplicates(colors: List[Color]) -> Tuple[List[Color], List[int]]: + if len(colors) == 0: + return ([], []) + colors_res = [colors[0]] + res_nb = [1] + for i in range(1, len(colors)): + if colors[i] != colors_res[-1]: + colors_res.append(colors[i]) + res_nb.append(1) + else: + res_nb[-1] += 1 + return (colors_res, res_nb) + +def reduplicate(colors: List[Color], repetitions: List[int]) -> List[Color]: + """ + Re-duplicates a list. Takes in a list of elements and a list of numbers of + repetitions. Concatetantes the repeted elements. + + Requires both lists to have the same length + """ + + res = [] + for i in range(len(colors)): + for _ in range(repetitions[i]): + res.append(colors[i]) + + return res + +class InterpType(Enum): + """ + Interpolation algorithm + """ + + NEAREST = 1 + LINEAR = 2 + +class Filling(Enum): + + VOID = 1 + EXTREMA = 2 + GREATEST = 3 + +def blendLedBar(colors: List[Color], + blending: Optional[InterpType] = InterpType.NEAREST, + filling: Optional[Filling] = Filling.GREATEST, + void_color: Optional[Color] = Color("black")) -> List[Color]: + total_len = len(colors) + (deduped, rep_nb) = remove_duplicates(colors) + if len(deduped) > LedBarLen: + # After dedup, there are still too many colors. Only show the first ones + logging.warning(f"LED bar interpolation: More than {LedBarLen} colors. Dropping colors") + return deduped[:LedBarLen] + + if len(colors) > LedBarLen: + # TODO: Try and dedup some colors to save space + return colors[:LedBarLen] + + if blending == InterpType.NEAREST: + factor = LedBarLen // total_len + + # First pass + for i in range(len(rep_nb)): + rep_nb[i] *= factor + + gap = LedBarLen - total_len * factor + + # TODO: Add GREATEST + # The idea is to first add a repetition to the already longest + # repetition to hurt the color ratio as little as possible + if gap == 0: + return reduplicate(deduped, rep_nb) + # If there is still + elif filling == Filling.VOID: + return (reduplicate(deduped, rep_nb) + ([void_color] * gap)) + elif filling == Filling.EXTREMA: + bot_gap = gap // 2 + top_gap = gap - bot_gap + return (([deduped[0]] * top_gap) + reduplicate(deduped, rep_nb) + + ([deduped[-1]] * bot_gap)) + diff --git a/shell.nix b/shell.nix index c3efeff..d021efa 100644 --- a/shell.nix +++ b/shell.nix @@ -21,6 +21,7 @@ in ps.black ps.pylint ps.ipython + ps.python-lsp-server ])) pkgs.pyright ];