107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
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):
|
|
"""
|
|
How we fill a bar when the color ratio is not a perfect match
|
|
"""
|
|
|
|
VOID = 1 # Fill with a "void" color
|
|
EXTREMA = 2 # Repeat both extrema values
|
|
GREATEST = 3 # Repeat the longest color streaks
|
|
|
|
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))
|
|
else:
|
|
# make the longest sequences longer
|
|
|
|
# sorted_indices is a list of indices in [0, len(rep_nb)) with
|
|
# increasing rep_nb[i]
|
|
sorted_indices = sorted(range(len(rep_nb)), key = lambda n: rep_nb[n])
|
|
# TODO: make this less ugly
|
|
while True:
|
|
for i in reversed(range(len(rep_nb))):
|
|
rep_nb[sorted_indices[i]] += 1
|
|
gap -= 1
|
|
if gap == 0:
|
|
return reduplicate(deduped, rep_nb)
|
|
|
|
|
|
|
|
|
|
|