led_pillar

Documentation: http://frombelow.net/projects/led_pillar/
Clone: git clone https://git.frombelow.net/led_pillar.git
Log | Files | Refs | Submodules | README | LICENSE

flowing_colors.py (6961B)


      1 #!/usr/bin/env python3
      2 
      3 from rpi_ws281x import Color
      4 import random
      5 import math
      6 import time
      7 
      8 import patterns
      9 
     10 # Utilitiy classes and functions
     11 
     12 
     13 class RandomWalker():
     14     """Walk towards random target values
     15 
     16     Returns fixed value if min==max."""
     17 
     18     def __init__(self, min, max, step):
     19         self.min = min
     20         self.max = max
     21         self.step = step
     22         if self.min == self.max:
     23             self.val = self.min
     24             self.target = self.min
     25         self.val = random.uniform(self.min, self.max)
     26         self.target = random.uniform(self.min, self.max)
     27 
     28     def next(self):
     29         if self.min != self.max:
     30             if abs(self.val - self.target) <= self.step*2:
     31                 self.target = random.uniform(self.min, self.max)
     32             if self.val <= self.target:
     33                 self.val += self.step
     34             else:
     35                 self.val -= self.step
     36         return self.val
     37 
     38 
     39 sin_norm_cache = {}
     40 def sin_norm(x, m_in, m_out):
     41     """Normalized sinus function: x in range [0..m_in] mapped to [0..m_out]
     42 
     43     x values > min are reduced mod m_in+1.
     44     """
     45     if (x % (m_in + 1), m_in, m_out) in sin_norm_cache:
     46         return(sin_norm_cache[(x % (m_in + 1), m_in, m_out)])
     47     else:
     48         rad = (float(x % (m_in + 1)) / (m_in + 1) * (2 * math.pi)) - math.pi
     49         out_percent = (math.sin(rad) + 1) / 2.0
     50         res = int(out_percent * m_out)
     51         sin_norm_cache.update({(x % (m_in + 1), m_in, m_out): res})
     52         return(res)
     53 
     54 
     55 def hsv2rgb(h, s, v):
     56     if h == None:
     57         return (0, 0, 0)
     58     else:
     59         hd = h / 60.0
     60         c =  s * v
     61         x = c * (1 - abs((hd % 2) - 1))
     62         if 0 <= hd <= 1:
     63             (r, g, b) = (c, x, 0)
     64         elif 1 < hd <= 2:
     65             (r, g, b) = (x, c, 0)
     66         elif 2 < hd <= 3:
     67             (r, g, b) = (0, c, x)
     68         elif 3 < hd <= 4:
     69             (r, g, b) = (0, x, c)
     70         elif 4 < hd <= 5:
     71             (r, g, b) = (x, 0, c)
     72         elif 5 < hd <= 6:
     73             (r, g, b) = (c, 0, x)
     74         m = v - c
     75         return map(lambda x: x*0xFF, (r+m, g+m, b+m))
     76 
     77 
     78 # Pattern classes
     79 
     80 
     81 class FlowingColorsRGB(patterns.Pattern):
     82     
     83     def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
     84         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)
     85         self.max_period = int(min(self.led_matrix_width, self.led_matrix_height)*2.0)
     86         self.min_period = int(min(self.led_matrix_width, self.led_matrix_height)*1.0)
     87         self.min_offset = 0
     88         self.max_offset = int(max(self.led_matrix_width, self.led_matrix_height)*1.0)
     89         # For each color channel, the period and the offset of the sinus function
     90         # mapping (x/y) values to color values changes in a random walk.
     91         self.channels = [
     92             # Hue x period
     93             [RandomWalker(min=self.min_period,
     94                           max=self.max_period,
     95                           step=self.max_period/100.0),
     96              # y period
     97              RandomWalker(min=self.min_period,
     98                           max=self.max_period,
     99                           step=self.max_period/100.0),
    100              # x offset
    101              RandomWalker(min=self.min_offset,
    102                           max=self.max_offset,
    103                           step=self.max_offset/100.0),
    104              # y offset
    105              RandomWalker(min=self.min_offset,
    106                           max=self.max_offset,
    107                           step=self.max_offset/100.0)] for _ in range(3)]
    108 
    109     def step(self):
    110         # Change period and offset
    111         for c in self.channels:
    112             for w in c:
    113                 w.next()
    114         r = self.sin_x_y_avg(period_offset=self.channels[0], m_out=255)
    115         g = self.sin_x_y_avg(period_offset=self.channels[1], m_out=255)
    116         b = self.sin_x_y_avg(period_offset=self.channels[2], m_out=255)
    117         for x in range(math.ceil(self.led_matrix_width)):
    118             for y in range((self.led_matrix_height)):
    119                 self.strip.setPixelColor(self.xy2i(x, y),
    120                                          Color(int(r[x][y]), int(g[x][y]), int(b[x][y])))
    121         self.strip.show()
    122         # time.sleep(.1)
    123 
    124     def sin_x_y_avg(self, period_offset, m_out):
    125         # period_offset are passed as instances of RandomWalker. We have to get the
    126         # values.
    127         (x_period, y_period, x_offset, y_offset) = map(lambda x: x.val, period_offset)
    128         pixel = [[None for _ in range(math.ceil(self.led_matrix_height))] for _ in range(math.ceil(self.led_matrix_width))]
    129         for x in range(math.ceil(self.led_matrix_width)):
    130             for y in range(math.ceil(self.led_matrix_height)):
    131                 pixel_x = sin_norm(x=x+x_offset, m_in=1.0*x_period, m_out=m_out)
    132                 pixel_y = sin_norm(x=y+y_offset, m_in=1.0*y_period, m_out=m_out)
    133                 pixel[x][y] = (pixel_x + pixel_y) / 2
    134         return pixel
    135 
    136 
    137 class FlowingColorsHSV(FlowingColorsRGB):
    138     def __init__(self, strip, led_matrix_width, led_matrix_height, led_strip_snake, opts=None):
    139         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)
    140         self.max_period = int(min(self.led_matrix_width, self.led_matrix_height)*3.0)
    141         self.min_period = int(min(self.led_matrix_width, self.led_matrix_height)*0.5)
    142         self.min_offset = 0
    143         self.max_offset = int(max(self.led_matrix_width, self.led_matrix_height)*20.0)
    144         self.hue_sin = [RandomWalker(min=self.min_period,
    145                                      max=self.max_period,
    146                                      step=self.max_period/1000.0),
    147                         # y period
    148                         RandomWalker(min=self.min_period,
    149                                      max=self.max_period,
    150                                      step=self.max_period/1000.0),
    151                         # x offset
    152                         RandomWalker(min=self.min_offset,
    153                                      max=self.max_offset,
    154                                      step=self.max_offset/10000.0),
    155                         # y offset
    156                         RandomWalker(min=self.min_offset,
    157                                      max=self.max_offset,
    158                                      step=self.max_offset/10000.0)]
    159         self.hue_offset = RandomWalker(min=0, max=360, step=1)
    160 
    161     def step(self):
    162         for w in self.hue_sin:
    163             w.next()
    164         self.hue_offset.next()
    165         h = self.sin_x_y_avg(period_offset=self.hue_sin, m_out=360)
    166         for x in range(math.ceil(self.led_matrix_width)):
    167             for y in range((self.led_matrix_height)):
    168                 (r, g, b) = hsv2rgb((h[x][y]+self.hue_offset.val) % 360, 1, 1)
    169                 self.strip.setPixelColor(self.xy2i(x, y), Color(int(r), int(g), int(b)))
    170         self.strip.show()
    171         # time.sleep(.1)