Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Light dimmer #105

Merged
merged 10 commits into from
Apr 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 56 additions & 40 deletions panther_lights/src/animation/battery_animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)

self._anim = np.zeros((self._anim_len, self.num_led))
self._h_min = 0.0
self._h_max = 120.0
self._h_min = 0.0 # red color
self._h_max = 120.0 / 360.0 # green color
self._resolution = 100
self._end_anim = 0.75 * self._resolution
self._start_fade = 0.80 * self._resolution
self._end_fade = 0.95 * self._resolution
self._gaussian_blur_radius = 1.5

def _update_frame(self) -> list:
Expand All @@ -31,51 +34,64 @@ def set_param(self, value: str) -> None:

def _create_anim(self, battery_percent: float) -> None:
battery_percent = np.clip(battery_percent, 0.0, 1.0)
# define basic HSV color
h = self._h_min
s = 1.0
v = 1.0
frame = np.zeros((self._num_led, 3), dtype=np.uint8)
anim = np.zeros((self._resolution, self._num_led, 3), dtype=np.uint8)

if self._num_led % 2 == 0:
percent_point = int(round(battery_percent * self._num_led / 2.0))
else:
percent_point = int(np.ceil(battery_percent * self._num_led / 2.0))

# 0.9 was added to hold for some time the final frame of the animation when percentage is 1.0
display_iterations = battery_percent * self._resolution * 0.9

if percent_point < 1:
percent_point = 1
display_iterations = 1

for i in range(self._resolution):
if i <= display_iterations:
anim_ind = round(i / self._resolution * self._num_led / 0.9)

if anim_ind < percent_point:
frame.fill(0)
ind_1 = int(np.ceil(self._num_led / 2.0 - anim_ind - 1.0))
ind_2 = int(np.floor(self._num_led / 2.0 + anim_ind))
h = (self._h_max - self._h_min) * np.sin(
max(0, battery_percent) * np.pi / 2.0
) * i / (display_iterations / 2.0) + self._h_min
rgb = np.array(hsv_to_rgb(h / 360.0, s, v))
frame[ind_1] = np.uint8(rgb * 255.0)
frame[ind_2] = np.uint8(rgb * 255.0)
elif percent_point <= anim_ind < 2 * percent_point:
ind_1 = int(np.ceil(self._num_led / 2.0 - 2.0 * percent_point + anim_ind))
ind_2 = int(
np.floor(self._num_led / 2.0 + 2.0 * percent_point - anim_ind - 1.0)
)
frame[ind_1] = np.uint8(rgb * 255.0)
frame[ind_2] = np.uint8(rgb * 255.0)
color_rising_duration = int(round(battery_percent * self._end_anim / 2.0))

if color_rising_duration < 1:
color_rising_duration = 1

for i in range(1, self._resolution):
if i <= color_rising_duration:
progress = i / color_rising_duration
frame = self._color_rising(battery_percent, progress)

elif i <= 2 * color_rising_duration:
progress = (i - color_rising_duration) / color_rising_duration
frame = self._fill_frame(frame, battery_percent, progress)

anim[i] = frame

if i > self._start_fade:
progress = (i - self._start_fade) / (self._end_fade - self._start_fade)
progress = min(1.0, progress)
anim[i] = (1 - progress) * frame

# filter to smooth animation and resize to match duration
img = Image.fromarray(anim)
img = img.filter(ImageFilter.GaussianBlur(self._gaussian_blur_radius))
img = img.resize((self._num_led, self._anim_len))
self._anim = np.array(img)

def _color_rising(self, battery_percent: float, progress: float) -> np.array:
frame = np.zeros((self._num_led, 3), dtype=np.uint8)
led_to_disp = battery_percent * self._num_led

middle_led = (self._num_led - 1) / 2
ind_1 = int(np.ceil(middle_led - progress * led_to_disp / 2.0))
ind_2 = int(np.floor(middle_led + progress * led_to_disp / 2.0))

rgb = self._calculate_color(battery_percent, progress)
frame[ind_1] = rgb
frame[ind_2] = rgb
return frame

def _fill_frame(self, frame: np.array, battery_percent: float, progress: float) -> np.array:
led_to_disp = battery_percent * self._num_led

middle_led = (self._num_led - 1) / 2
ind_1 = int(np.ceil(middle_led - (1 - progress) * led_to_disp / 2.0))
ind_2 = int(np.floor(middle_led + (1 - progress) * led_to_disp / 2.0))

rgb = self._calculate_color(battery_percent)
frame[ind_1] = rgb
frame[ind_2] = rgb
return frame

def _calculate_color(self, battery_percent: float, progress: float = 1.0) -> tuple:
h = (self._h_max - self._h_min) * np.sin(
battery_percent * np.pi / 2.0
) * progress + self._h_min
rgb = np.array(hsv_to_rgb(h, 1.0, 1.0))
rgb = np.uint8(rgb * 255.0)
return rgb
Loading