pyjecteur/pyjecteur/common.py

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)