Skip to content

Commit 773c5b7

Browse files
authored
Merge pull request #23 from rhooper/rainbowsparkle-fix
Rainbowsparkle fix
2 parents cb18147 + 5f5d518 commit 773c5b7

File tree

13 files changed

+117
-98
lines changed

13 files changed

+117
-98
lines changed

adafruit_led_animation/animation/__init__.py

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,13 @@ class Animation:
5454
"""
5555
Base class for animations.
5656
"""
57-
cycle_complete_supported = False
57+
on_cycle_complete_supported = False
5858

5959
# pylint: disable=too-many-arguments
6060
def __init__(self, pixel_object, speed, color, peers=None, paused=False, name=None):
6161
self.pixel_object = pixel_object
6262
self.pixel_object.auto_write = False
63-
self.peers = peers if peers else []
64-
"""A sequence of animations to trigger .draw() on when this animation draws."""
63+
self._peers = [self] + peers if peers is not None else [self]
6564
self._speed_ns = 0
6665
self._color = None
6766
self._paused = paused
@@ -71,6 +70,7 @@ def __init__(self, pixel_object, speed, color, peers=None, paused=False, name=No
7170
self.speed = speed # sets _speed_ns
7271
self.color = color # Triggers _recompute_color
7372
self.name = name
73+
self.cycle_complete = False
7474
self.notify_cycles = 1
7575
"""Number of cycles to trigger additional cycle_done notifications after"""
7676
self.draw_count = 0
@@ -95,30 +95,59 @@ def animate(self):
9595
if now < self._next_update:
9696
return False
9797

98-
self.draw()
99-
self.draw_count += 1
100-
10198
# Draw related animations together
102-
if self.peers:
103-
for peer in self.peers:
104-
peer.draw()
99+
for anim in self._peers:
100+
anim.draw()
101+
anim.after_draw()
102+
103+
for anim in self._peers:
104+
anim.show()
105+
106+
# Note that the main animation cycle_complete flag is used, not the peer flag.
107+
for anim in self._peers:
108+
if self.cycle_complete:
109+
anim.on_cycle_complete()
110+
anim.cycle_complete = False
105111

106112
self._next_update = now + self._speed_ns
107113
return True
108114

109115
def draw(self):
110116
"""
111117
Animation subclasses must implement draw() to render the animation sequence.
112-
Draw must call show().
118+
Animations should not call show(), as animate() will do so, after after_draw().
119+
Animations should set .cycle_done = True when an animation cycle is completed.
113120
"""
114121
raise NotImplementedError()
115122

123+
def after_draw(self):
124+
"""
125+
Animation subclasses may implement after_draw() to do operations after the main draw()
126+
is called.
127+
"""
128+
116129
def show(self):
117130
"""
118131
Displays the updated pixels. Called during animates with changes.
119132
"""
120133
self.pixel_object.show()
121134

135+
@property
136+
def peers(self):
137+
"""
138+
Get the animation's peers. Peers are drawn, then shown together.
139+
"""
140+
return self._peers[1:]
141+
142+
@peers.setter
143+
def peers(self, peer_list):
144+
"""
145+
Set the animation's peers.
146+
:param list peer_list: List of peer animations.
147+
"""
148+
if peer_list is not None:
149+
self._peers = [self] + peer_list
150+
122151
def freeze(self):
123152
"""
124153
Stops the animation until resumed.
@@ -173,7 +202,7 @@ def _recompute_color(self, color):
173202
Override as needed.
174203
"""
175204

176-
def cycle_complete(self):
205+
def on_cycle_complete(self):
177206
"""
178207
Called by some animations when they complete an animation cycle.
179208
Animations that support cycle complete notifications will have X property set to False.

adafruit_led_animation/animation/chase.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def _resetter():
8383

8484
super().__init__(pixel_object, speed, color, name=name)
8585

86-
cycle_complete_supported = True
86+
on_cycle_complete_supported = True
8787

8888
@property
8989
def reverse(self):
@@ -115,10 +115,9 @@ def bar_colors():
115115

116116
colorgen = bar_colors()
117117
self.pixel_object[:] = [next(colorgen) for _ in self.pixel_object]
118-
self.show()
119118

120119
if self.draw_count % len(self.pixel_object) == 0:
121-
self.cycle_complete()
120+
self.cycle_complete = True
122121
self._offset = (self._offset + self._direction) % self._repeat_width
123122

124123
def bar_color(self, n, pixel_no=0): # pylint: disable=unused-argument

adafruit_led_animation/animation/colorcycle.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,10 @@ def __init__(self, pixel_object, speed, colors=RAINBOW, name=None):
6464
self._generator = self._color_generator()
6565
next(self._generator)
6666

67-
cycle_complete_supported = True
67+
on_cycle_complete_supported = True
6868

6969
def draw(self):
7070
self.pixel_object.fill(self.color)
71-
self.show()
7271
next(self._generator)
7372

7473
def _color_generator(self):
@@ -78,7 +77,7 @@ def _color_generator(self):
7877
yield
7978
index = (index + 1) % len(self.colors)
8079
if index == 0:
81-
self.cycle_complete()
80+
self.cycle_complete = True
8281

8382
def reset(self):
8483
"""

adafruit_led_animation/animation/comet.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __init__(
8989
self._generator = self._comet_generator()
9090
super().__init__(pixel_object, speed, color, name=name)
9191

92-
cycle_complete_supported = True
92+
on_cycle_complete_supported = True
9393

9494
def _recompute_color(self, color):
9595
pass
@@ -130,13 +130,12 @@ def _comet_generator(self):
130130
]
131131
else:
132132
self.pixel_object[start : start + end] = colors[0:end]
133-
self.show()
134133
yield
135134
cycle_passes += 1
136135
if self.bounce:
137136
self.reverse = not self.reverse
138137
if not self.bounce or cycle_passes == 2:
139-
self.cycle_complete()
138+
self.cycle_complete = True
140139
cycle_passes = 0
141140

142141
def draw(self):

adafruit_led_animation/animation/pulse.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,11 @@ def __init__(self, pixel_object, speed, color, period=5, name=None):
6464
self._generator = None
6565
self.reset()
6666

67-
cycle_complete_supported = True
67+
on_cycle_complete_supported = True
6868

6969
def draw(self):
7070
color = next(self._generator)
71-
self.fill(color)
72-
self.show()
71+
self.pixel_object.fill(color)
7372

7473
def reset(self):
7574
"""
@@ -78,8 +77,13 @@ def reset(self):
7877
white = len(self.pixel_object[0]) > 3 and isinstance(
7978
self.pixel_object[0][-1], int
8079
)
80+
dotstar = len(self.pixel_object[0]) == 4 and isinstance(
81+
self.pixel_object[0][-1], float
82+
)
8183
from adafruit_led_animation.helper import ( # pylint: disable=import-outside-toplevel
8284
pulse_generator,
8385
)
8486

85-
self._generator = pulse_generator(self._period, self, white)
87+
self._generator = pulse_generator(
88+
self._period, self, white, dotstar_pwm=dotstar
89+
)

adafruit_led_animation/animation/rainbow.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def generate_rainbow(self):
8585
self.colors.append(colorwheel(int(i)))
8686
i += self._step
8787

88-
cycle_complete_supported = True
88+
on_cycle_complete_supported = True
8989

9090
def _color_wheel_generator(self):
9191
period = int(self._period * NANOS_PER_SECOND)
@@ -113,9 +113,8 @@ def _color_wheel_generator(self):
113113
colorwheel((i + wheel_index) % 255) for i in range(num_pixels)
114114
]
115115
self._wheel_index = wheel_index
116-
self.show()
117116
if cycle_completed:
118-
self.cycle_complete()
117+
self.cycle_complete = True
119118
yield
120119

121120
def _draw_precomputed(self, num_pixels, wheel_index):

adafruit_led_animation/animation/rainbowchase.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ def __init__(
7878
super().__init__(pixel_object, speed, 0, size, spacing, reverse, name)
7979

8080
def bar_color(self, n, pixel_no=0):
81-
return self._colors[self._color_idx - n]
81+
return self._colors[self._color_idx - (n % len(self._colors))]
8282

83-
def cycle_complete(self):
83+
def on_cycle_complete(self):
8484
self._color_idx = (self._color_idx + self._direction) % len(self._colors)
85-
super().cycle_complete()
85+
super().on_cycle_complete()

adafruit_led_animation/animation/rainbowsparkle.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ def generate_rainbow(self):
106106
int(self._background_brightness * color[2]),
107107
)
108108

109-
def show(self):
109+
def after_draw(self):
110+
self.show()
110111
pixels = [
111112
random.randint(0, len(self.pixel_object) - 1)
112113
for n in range(self._num_sparkles)
@@ -115,4 +116,3 @@ def show(self):
115116
self.pixel_object[pixel] = self._bright_colors[
116117
(self._wheel_index + pixel) % len(self._bright_colors)
117118
]
118-
super().show()

adafruit_led_animation/animation/sparkle.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ class Sparkle(Animation):
6464
def __init__(self, pixel_object, speed, color, num_sparkles=1, name=None):
6565
if len(pixel_object) < 2:
6666
raise ValueError("Sparkle needs at least 2 pixels")
67-
self._half_color = None
68-
self._dim_color = None
67+
self._half_color = color
68+
self._dim_color = color
69+
self._sparkle_color = color
6970
self._num_sparkles = num_sparkles
71+
self._pixels = []
7072
super().__init__(pixel_object, speed, color, name=name)
7173

7274
def _recompute_color(self, color):
@@ -79,16 +81,18 @@ def _recompute_color(self, color):
7981
self.pixel_object[pixel] = dim_color
8082
self._half_color = half_color
8183
self._dim_color = dim_color
84+
self._sparkle_color = color
8285

8386
def draw(self):
84-
pixels = [
87+
self._pixels = [
8588
random.randint(0, (len(self.pixel_object) - 2))
86-
for n in range(self._num_sparkles)
89+
for _ in range(self._num_sparkles)
8790
]
88-
for pixel in pixels:
89-
self.pixel_object[pixel] = self._color
91+
for pixel in self._pixels:
92+
self.pixel_object[pixel] = self._sparkle_color
93+
94+
def after_draw(self):
9095
self.show()
91-
for pixel in pixels:
96+
for pixel in self._pixels:
9297
self.pixel_object[pixel] = self._half_color
9398
self.pixel_object[pixel + 1] = self._dim_color
94-
self.show()

adafruit_led_animation/animation/sparklepulse.py

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,13 @@
4444
4545
"""
4646

47-
import random
48-
from adafruit_led_animation import NANOS_PER_SECOND, monotonic_ns
49-
from adafruit_led_animation.animation import Animation
47+
from adafruit_led_animation.animation.sparkle import Sparkle
48+
from adafruit_led_animation.helper import pulse_generator
5049

5150

52-
class SparklePulse(Animation):
51+
class SparklePulse(Sparkle):
5352
"""
54-
Combination of the Spark and Pulse animations.
53+
Combination of the Sparkle and Pulse animations.
5554
5655
:param pixel_object: The initialised LED object.
5756
:param int speed: Animation refresh rate in seconds, e.g. ``0.1``.
@@ -63,48 +62,30 @@ class SparklePulse(Animation):
6362

6463
# pylint: disable=too-many-arguments
6564
def __init__(
66-
self, pixel_object, speed, color, period=5, max_intensity=1, min_intensity=0
65+
self,
66+
pixel_object,
67+
speed,
68+
color,
69+
period=5,
70+
max_intensity=1,
71+
min_intensity=0,
72+
name=None,
6773
):
68-
if len(pixel_object) < 2:
69-
raise ValueError("Sparkle needs at least 2 pixels")
70-
self.max_intensity = max_intensity
71-
self.min_intensity = min_intensity
74+
self._max_intensity = max_intensity
75+
self._min_intensity = min_intensity
7276
self._period = period
73-
self._intensity_delta = max_intensity - min_intensity
74-
self._half_period = period / 2
75-
self._position_factor = 1 / self._half_period
76-
self._bpp = len(pixel_object[0])
77-
self._last_update = monotonic_ns()
78-
self._cycle_position = 0
79-
self._half_color = None
80-
self._dim_color = None
81-
super().__init__(pixel_object, speed, color)
82-
83-
def _recompute_color(self, color):
84-
half_color = tuple(color[rgb] // 4 for rgb in range(len(color)))
85-
dim_color = tuple(color[rgb] // 10 for rgb in range(len(color)))
86-
for pixel in range(len(self.pixel_object)):
87-
if self.pixel_object[pixel] == self._half_color:
88-
self.pixel_object[pixel] = half_color
89-
elif self.pixel_object[pixel] == self._dim_color:
90-
self.pixel_object[pixel] = dim_color
91-
self._half_color = half_color
92-
self._dim_color = dim_color
77+
white = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], int)
78+
dotstar = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], float)
79+
super().__init__(
80+
pixel_object, speed=speed, color=color, num_sparkles=1, name=name
81+
)
82+
self._generator = pulse_generator(
83+
self._period, self, white, dotstar_pwm=dotstar
84+
)
9385

9486
def draw(self):
95-
pixel = random.randint(0, (len(self.pixel_object) - 2))
96-
97-
now = monotonic_ns()
98-
time_since_last_draw = (now - self._last_update) / NANOS_PER_SECOND
99-
self._last_update = now
100-
pos = self._cycle_position = (
101-
self._cycle_position + time_since_last_draw
102-
) % self._period
103-
if pos > self._half_period:
104-
pos = self._period - pos
105-
intensity = self.min_intensity + (
106-
pos * self._intensity_delta * self._position_factor
107-
)
108-
color = [int(self.color[n] * intensity) for n in range(self._bpp)]
109-
self.pixel_object[pixel] = color
87+
self._sparkle_color = next(self._generator)
88+
super().draw()
89+
90+
def after_draw(self):
11091
self.show()

adafruit_led_animation/group.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,15 @@ def __init__(self, *members, sync=False, name=None):
7979

8080
# Catch cycle_complete on the last animation.
8181
self._members[-1].add_cycle_complete_receiver(self._group_done)
82-
self.cycle_complete_supported = self._members[-1].cycle_complete_supported
82+
self.on_cycle_complete_supported = self._members[-1].on_cycle_complete_supported
8383

8484
def __str__(self):
8585
return "<AnimationGroup %s: %s>" % (self.__class__.__name__, self.name)
8686

8787
def _group_done(self, animation): # pylint: disable=unused-argument
88-
self.cycle_complete()
88+
self.on_cycle_complete()
8989

90-
def cycle_complete(self):
90+
def on_cycle_complete(self):
9191
"""
9292
Called by some animations when they complete an animation cycle.
9393
Animations that support cycle complete notifications will have X property set to False.

0 commit comments

Comments
 (0)