commit d946424604e3872f9e28c087d26e82a22f5100df
parent 4bebb2e955333a0d5188d1cceb643f5b98c837ec
Author: Gerd Beuster <gerd@frombelow.net>
Date: Sat, 11 Apr 2020 09:06:58 +0200
Flowing color patterns (RGB and HSV) added
Diffstat:
4 files changed, 196 insertions(+), 13 deletions(-)
diff --git a/blue_green_flames.py b/blue_green_flames.py
@@ -56,13 +56,13 @@ class BlueGreenFlames(patterns.Pattern):
particlesperframe = 1,
particlelife = 100,
drawtype = PyIgnition.DRAWTYPE_CIRCLE,
- colour = (50, 20, 200),
+ colour = (65, 25, 255),
radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(1, colour = (50, 20, 200),
+ new_flame.CreateParticleKeyframe(1, colour = (65, 25, 255),
radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(3, colour = (0, 0, 150),
+ new_flame.CreateParticleKeyframe(3, colour = (0, 0, 190),
radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
- new_flame.CreateParticleKeyframe(6, colour = (20, 20, 50),
+ new_flame.CreateParticleKeyframe(6, colour = (25, 25, 65),
radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
new_flame.CreateParticleKeyframe(9, colour = (0, 0, 0),
radius = self.led_matrix_width/32.0*blue_flames_scale_factor)
@@ -77,13 +77,13 @@ class BlueGreenFlames(patterns.Pattern):
initdirectionrandrange = 0.5,
particlesperframe = 1, particlelife = 100,
drawtype = PyIgnition.DRAWTYPE_CIRCLE,
- colour = (255, 200, 100),
+ colour = (255, 255, 130),
radius = self.led_matrix_width/36.0)
- self.green_flame.CreateParticleKeyframe(5, colour = (50, 200, 20),
+ self.green_flame.CreateParticleKeyframe(5, colour = (65, 255, 25),
radius = self.led_matrix_width/36.0)
- self.green_flame.CreateParticleKeyframe(10, colour = (0, 150, 0),
+ self.green_flame.CreateParticleKeyframe(10, colour = (0, 190, 0),
radius = self.led_matrix_width/36.0)
- self.green_flame.CreateParticleKeyframe(15, colour = (20, 50, 20),
+ self.green_flame.CreateParticleKeyframe(15, colour = (25, 65, 25),
radius = self.led_matrix_width/36.0)
self.green_flame.CreateParticleKeyframe(20, colour = (0, 0, 0),
radius = self.led_matrix_width/36.0)
diff --git a/flowing_colors.py b/flowing_colors.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python3
+
+from rpi_ws281x import Color
+import random
+import math
+import time
+
+import patterns
+
+# Utilitiy classes and functions
+
+
+class RandomWalker():
+ """Walk towards random target values
+
+ Returns fixed value if min==max."""
+
+ def __init__(self, min, max, step):
+ self.min = min
+ self.max = max
+ self.step = step
+ if self.min == self.max:
+ self.val = self.min
+ self.target = self.min
+ self.val = random.uniform(self.min, self.max)
+ self.target = random.uniform(self.min, self.max)
+
+ def next(self):
+ if self.min != self.max:
+ if abs(self.val - self.target) <= self.step*2:
+ self.target = random.uniform(self.min, self.max)
+ if self.val <= self.target:
+ self.val += self.step
+ else:
+ self.val -= self.step
+ return self.val
+
+
+sin_norm_cache = {}
+def sin_norm(x, m_in, m_out):
+ """Normalized sinus function: x in range [0..m_in] mapped to [0..m_out]
+
+ x values > min are reduced mod m_in+1.
+ """
+ if (x % (m_in + 1), m_in, m_out) in sin_norm_cache:
+ return(sin_norm_cache[(x % (m_in + 1), m_in, m_out)])
+ else:
+ rad = (float(x % (m_in + 1)) / (m_in + 1) * (2 * math.pi)) - math.pi
+ out_percent = (math.sin(rad) + 1) / 2.0
+ res = int(out_percent * m_out)
+ sin_norm_cache.update({(x % (m_in + 1), m_in, m_out): res})
+ return(res)
+
+
+def hsv2rgb(h, s, v):
+ if h == None:
+ return (0, 0, 0)
+ else:
+ hd = h / 60.0
+ c = s * v
+ x = c * (1 - abs((hd % 2) - 1))
+ if 0 <= hd <= 1:
+ (r, g, b) = (c, x, 0)
+ elif 1 < hd <= 2:
+ (r, g, b) = (x, c, 0)
+ elif 2 < hd <= 3:
+ (r, g, b) = (0, c, x)
+ elif 3 < hd <= 4:
+ (r, g, b) = (0, x, c)
+ elif 4 < hd <= 5:
+ (r, g, b) = (x, 0, c)
+ elif 5 < hd <= 6:
+ (r, g, b) = (c, 0, x)
+ m = v - c
+ return map(lambda x: x*0xFF, (r+m, g+m, b+m))
+
+
+# Pattern classes
+
+
+class FlowingColorsRGB(patterns.Pattern):
+
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ patterns.Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
+ self.max_period = int(min(self.led_matrix_width, self.led_matrix_height)*2.0)
+ self.min_period = int(min(self.led_matrix_width, self.led_matrix_height)*1.0)
+ self.min_offset = 0
+ self.max_offset = int(max(self.led_matrix_width, self.led_matrix_height)*1.0)
+ # For each color channel, the period and the offset of the sinus function
+ # mapping (x/y) values to color values changes in a random walk.
+ self.channels = [
+ # Hue x period
+ [RandomWalker(min=self.min_period,
+ max=self.max_period,
+ step=self.max_period/100.0),
+ # y period
+ RandomWalker(min=self.min_period,
+ max=self.max_period,
+ step=self.max_period/100.0),
+ # x offset
+ RandomWalker(min=self.min_offset,
+ max=self.max_offset,
+ step=self.max_offset/100.0),
+ # y offset
+ RandomWalker(min=self.min_offset,
+ max=self.max_offset,
+ step=self.max_offset/100.0)] for _ in range(3)]
+
+ def step(self):
+ # Change period and offset
+ for c in self.channels:
+ for w in c:
+ w.next()
+ r = self.sin_x_y_avg(period_offset=self.channels[0], m_out=255)
+ g = self.sin_x_y_avg(period_offset=self.channels[1], m_out=255)
+ b = self.sin_x_y_avg(period_offset=self.channels[2], m_out=255)
+ for x in range(math.ceil(self.led_matrix_width)):
+ for y in range((self.led_matrix_height)):
+ self.strip.setPixelColor(self.xy2i(x, y),
+ Color(int(r[x][y]), int(g[x][y]), int(b[x][y])))
+ self.strip.show()
+ time.sleep(.1)
+
+ def sin_x_y_avg(self, period_offset, m_out):
+ # period_offset are passed as instances of RandomWalker. We have to get the
+ # values.
+ (x_period, y_period, x_offset, y_offset) = map(lambda x: x.val, period_offset)
+ pixel = [[None for _ in range(math.ceil(self.led_matrix_height))] for _ in range(math.ceil(self.led_matrix_width))]
+ for x in range(math.ceil(self.led_matrix_width)):
+ for y in range(math.ceil(self.led_matrix_height)):
+ pixel_x = sin_norm(x=x+x_offset, m_in=1.0*x_period, m_out=m_out)
+ pixel_y = sin_norm(x=y+y_offset, m_in=1.0*y_period, m_out=m_out)
+ pixel[x][y] = (pixel_x + pixel_y) / 2
+ return pixel
+
+
+class FlowingColorsHSV(FlowingColorsRGB):
+ def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
+ patterns.Pattern.__init__(self, strip=strip, led_matrix_width=led_matrix_width, led_matrix_height=led_matrix_height, led_strip_snake = led_strip_snake, opts=opts)
+ self.max_period = int(min(self.led_matrix_width, self.led_matrix_height)*3.0)
+ self.min_period = int(min(self.led_matrix_width, self.led_matrix_height)*0.5)
+ self.min_offset = 0
+ self.max_offset = int(max(self.led_matrix_width, self.led_matrix_height)*20.0)
+ self.hue_sin = [RandomWalker(min=self.min_period,
+ max=self.max_period,
+ step=self.max_period/1000.0),
+ # y period
+ RandomWalker(min=self.min_period,
+ max=self.max_period,
+ step=self.max_period/1000.0),
+ # x offset
+ RandomWalker(min=self.min_offset,
+ max=self.max_offset,
+ step=self.max_offset/10000.0),
+ # y offset
+ RandomWalker(min=self.min_offset,
+ max=self.max_offset,
+ step=self.max_offset/10000.0)]
+ self.hue_offset = RandomWalker(min=0, max=360, step=1)
+
+ def step(self):
+ for w in self.hue_sin:
+ w.next()
+ self.hue_offset.next()
+ h = self.sin_x_y_avg(period_offset=self.hue_sin, m_out=360)
+ for x in range(math.ceil(self.led_matrix_width)):
+ for y in range((self.led_matrix_height)):
+ (r, g, b) = hsv2rgb((h[x][y]+self.hue_offset.val) % 360, 1, 1)
+ self.strip.setPixelColor(self.xy2i(x, y), Color(int(r), int(g), int(b)))
+ self.strip.show()
+ time.sleep(.1)
diff --git a/main.py b/main.py
@@ -10,6 +10,7 @@ import pdb
import patterns
import cellular_automaton
import blue_green_flames
+import flowing_colors
# LED strip/matrix configuration
@@ -41,9 +42,10 @@ LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10 # DMA channel to use for generating signal (try 10)
# Set to 0 for darkest and 255 for brightest
# LED_BRIGHTNESS = 255
-LED_BRIGHTNESS = 16
-# LED_BRIGHTNESS = 8
+# LED_BRIGHTNESS = 16
+LED_BRIGHTNESS = 8
# LED_BRIGHTNESS = 4
+# LED_BRIGHTNESS = 2
# LED_BRIGHTNESS = 1
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53
@@ -61,11 +63,15 @@ MODE_RGB = 0
MODE_WORM = 1
MODE_CELLULAR_AUTOMATON = 2
MODE_BLUE_GREEN_FLAMES = 3
-MODE_TEST_PATTERN = 4
+MODE_FLOW_RGB = 4
+MODE_FLOW_HSV = 5
+MODE_TEST_PATTERN = 6
mode = [MODE_BLUE_GREEN_FLAMES]
-# mode = [MODE_WORM]
-# mode = [MODE_CELLULAR_AUTOMATON]
+#mode = [MODE_WORM]
+#mode = [MODE_CELLULAR_AUTOMATON]
+#mode = [MODE_FLOW_RGB]
+#mode = [MODE_FLOW_HSV]
def run(cmd_queue=None):
global mode
@@ -89,6 +95,8 @@ def run(cmd_queue=None):
MODE_WORM : patterns.Worm,
MODE_CELLULAR_AUTOMATON : cellular_automaton.CellularAutomaton,
MODE_BLUE_GREEN_FLAMES : blue_green_flames.BlueGreenFlames,
+ MODE_FLOW_RGB : flowing_colors.FlowingColorsRGB,
+ MODE_FLOW_HSV : flowing_colors.FlowingColorsHSV,
MODE_TEST_PATTERN : patterns.TestPattern
}[current_mode[0]](strip, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT, LED_STRIP_SNAKE, current_mode[1:])
p.step()
@@ -120,6 +128,8 @@ def hello_world():
'worm': [MODE_WORM],
'cellular': [MODE_CELLULAR_AUTOMATON],
'flames': [MODE_BLUE_GREEN_FLAMES],
+ 'flow_rgb': [MODE_FLOW_RGB],
+ 'flow_hsv': [MODE_FLOW_HSV],
'test': [MODE_TEST_PATTERN]
}[mode]
cmd_queue.put(mode_number)
diff --git a/templates/index.html b/templates/index.html
@@ -30,6 +30,8 @@ button{font-size : 190%; width:10em;}
<button type="submit" name="mode" value="worm">Wurm</button><br /><br />
<button type="submit" name="mode" value="cellular">Zellulärer Automat</button><br /><br />
<button type="submit" name="mode" value="flames">Feuer</button><br /><br />
+ <button type="submit" name="mode" value="flow_rgb">Flow (RGB)</button><br /><br />
+ <button type="submit" name="mode" value="flow_hsv">Flow (HSV)</button><br /><br />
<button type="submit" name="mode" value="test">Testmuster</button><br /><br />
</form>
<p><a href="config.html"</a>Konfigurieren</a></p>