def heat_to_rgb(temperature): t192 = round((temperature / 255.0) * 191) heatramp = t192 & 0x3F heatramp <<= 2 if t192 > 0x80: return RGB(r=255, g=255, b=heatramp, a=255) elif t192 > 0x40: return RGB(r=255, g=heatramp, b=0, a=255) else: return RGB(r=heatramp, g=0, b=0, a=255)
def __init__(self, **kwargs): super().__init__(**kwargs) self.pattern_name = "Storm" self.rain = Modifier('rain intensity', 10.0, minimum=0, maximum=0.2) self.thunder = Modifier('thunder intensity', 5.0, minimum=0, maximum=0.2) self.thunder_size = Modifier('thunder size', 80, minimum=0, maximum=self.strip_length) self.thunder_color = RGB(white=True) self.rain_color = RGB(r=70, g=100, b=255, a=255) self.thunder_centers = {} for idx in range(self.strip_length): self.pixels[idx]['rain'] = 0
def __init__(self, **kwargs): kwargs['color'] = RGB() super().__init__(**kwargs) self.pattern_name = "FireWork" self.increment = Modifier('fire power', 10.0, minimum=0.001, maximum=0.25) self.spark = Modifier('sparks', 10.0, minimum=0, maximum=1 / self.strip_length) for idx in range(self.strip_length): self.pixels[idx]['timestep'] = 0 self.centers = [] self.modifiers = dict( increment=self.increment, spark=self.spark, )
def fill(self): r_phi = self.r_phi() b_phi = self.b_phi() g_phi = self.g_phi() if not self.r_phi() == 0: r_phi = pi / r_phi if not self.b_phi() == 0: b_phi = pi / b_phi if not self.g_phi() == 0: g_phi = pi / g_phi for idx in range(self.strip_length): idx2degree = scale(idx + self.counter, 0, self.max_range(), 0, self.strip_length) r = sin(idx2degree + r_phi) g = sin(idx2degree + g_phi) b = sin(idx2degree + b_phi) r = scale(r, 0, 255, -1, 1) g = scale(g, 0, 255, -1, 1) b = scale(b, 0, 255, -1, 1) r = floor(r) g = floor(g) b = floor(b) self.pixels[idx]['color'] = RGB(r=r, g=g, b=b, a=self.color.a) self.counter += 1 self.counter %= self.strip_length * 255
def empty_center(self): """ Return an empty center point as a dict with fields :return: """ if self.randomize_color: default_dict = dict(color=RGB(random=True), alpha=0, delay=randint(0, 100), increasing=True) else: default_dict = dict(color=RGB(rgb=self.color), alpha=0, delay=randint(0, 100), increasing=True) # if there is no start in delay then alpha is maximum if not self.rate_start: default_dict['alpha'] = 255 return default_dict
def __init__(self, handler, rate, pixels, color=RGB()): """ :param handler: The handler for the led strip, either a DotStar_Emulator.emulator.send_test_data.App or a rpi.pi_handler.PiHandler :param rate:(float) the rate for the pixel update :param pixels: (int) the number of pixels :param color: (default RGB), the initial color for the leds """ # init the thread and the handler threading.Thread.__init__(self, name="PatternThread") self.handler = handler self.rate = Modifier("rate", float(rate), minimum=0.0, maximum=1.5) self.stop = False self.strip_length = pixels self.color = color self.alpha = 255 # boolean value to randomize color self.randomize_color = False # string for patter name self.pattern_name = None # dictionary storing the modifiers to be implemented in the web app self.modifiers = dict() # init and set the pixels to the default color self.pixels = {idx: Pixel(index=idx, color=self.color.copy(), set_func=self.color_set) for idx in range(self.strip_length + 1)}
def color_idx(self, idx): # if the current one is alive then color it if self.alive[idx]: if self.randomize_color: self.pixels[idx]['color'] = RGB(random=True) else: self.pixels[idx]['color'] = self.color # else kill it else: self.pixels[idx]['color'] = (0, 0, 0, 0)
def get_rgba(self, data=None): """ Return rgb class from the database :param data: dict, optional database :return: RGB class """ if data is None: data = self.get("RGBA") return RGB(r=data["r"], g=data['g'], b=data['b'], a=data['a'], random=data['random'])
def fill(self): new_pixels = copy(self.pixels) # increase tails for every center for idx in range(len(self.centers)): cntr, stop = self.centers[idx] ### back motion prev_pixel = new_pixels[(cntr - stop + 1) % self.strip_length] cur_pixel = new_pixels[(cntr - stop) % self.strip_length] color, timestep = drop([prev_pixel, cur_pixel], self.increment()) ts = max(timestep - self.increment(), 0.0) cur_pixel['timestep'] = ts cur_pixel['color'] = color new_pixels[(cntr - stop) % self.strip_length] = cur_pixel ### forward motion prev_pixel = new_pixels[(cntr + stop - 1) % self.strip_length] cur_pixel = new_pixels[(cntr + stop) % self.strip_length] color, timestep = drop([prev_pixel, cur_pixel], self.increment()) ts = max(timestep - self.increment(), 0.0) cur_pixel['timestep'] = ts cur_pixel['color'] = color new_pixels[(cntr + stop) % self.strip_length] = cur_pixel # increment stop for next iteration self.centers[idx] = (cntr, stop + 1) # remove all centers with a maximum stop self.centers = [ elem for elem in self.centers if elem[1] < 1 // self.increment() ] # fade all leds for idx in range(self.strip_length): new_pixels[idx]['color'].fade(self.increment()) # either update pixels or spark for idx in range(self.strip_length): if random() < self.spark(): c = RGB(random=True) self.pixels[idx]['color'] = c self.pixels[idx]['timestep'] = 1.0 self.centers.append((idx, 1)) else: self.pixels[idx]['color'] = new_pixels[idx]['color'] self.pixels[idx]['timestep'] = new_pixels[idx]['timestep']
def fill(self): step = self.step color = self.color if self.reverse: color = RGB() for idx in range(self.strip_length): if idx < step: self.pixels[idx]['color'] = color self.step += 1 if self.step > self.strip_length: self.reverse = not self.reverse self.step = 0
def drop(pixels, inc): """ Perform an average on a list of pixels :param pixels: list[dict], list of pixels with 'color' and 'timestep' keys :param inc: float, increment :return: tuple[RGB,int]: color, timestep """ tots = [ elem['timestep'] for elem in pixels if not elem['color'].is_black() ] tot = sum(tots) if tot == 0: tot = 0.000001 # perform weighed average for rgba red = [elem['color'].r * elem['timestep'] for elem in pixels] red = sum(red) / tot green = [elem['color'].g * elem['timestep'] for elem in pixels] green = sum(green) / tot blue = [elem['color'].b * elem['timestep'] for elem in pixels] blue = sum(blue) / tot alpha = [elem['color'].a * elem['timestep'] for elem in pixels] alpha = sum(alpha) / tot # bound max and min red = max(red, 0) green = max(green, 0) blue = max(blue, 0) alpha = max(alpha, 0) red = min(red, 255) green = min(green, 255) blue = min(blue, 255) alpha = min(alpha, 255) # if there are no tots, use zero as average timestep try: ts = max(tots) except ValueError: ts = 0 return RGB(r=red, g=green, b=blue, a=alpha).fade(inc), ts
def fill(self): # reset counter if self.counter == sys.maxsize - 1: self.counter = 0 # color for idx in range(self.strip_length): val = self.op.noise2d(idx / self.x_div(), self.counter / self.y_div()) val = scale(val, 0, 255, -1, 1) # add base value + deepness val = bound_add(val, self.deepness()) self.pixels[idx]['color'] = RGB(b=val, g=255 - val, a=self.color.a) # update counter self.counter += 1
def listener_method(self, event): """ Main listener, called when db is updated :param event: dict :return: None """ if not super(FireBaseController, self).listener_method(event): return to_log = f"{event.event_type}\n{event.path}\n{event.data}" fire_logger.debug(to_log) path = event.path data = event.data if "rate" in path: self.pattern.set_rate(data) # stop and restart pattern if required elif "cur_pattern" in path: # get values pattern_choice = data rate = self.get_rate() rgba = self.get_rgba() # stop and restart self.pattern.close() self.pattern = Patterns[pattern_choice](rate=rate, color=rgba, handler=self.handler, pixels=self.pixels) self.pattern.color_all(RGB()) self.pattern.start() # update rgba elif "RGBA" in path: self.process_rgba(path, data) # update pattern attributes elif "pattern_attributes" in path: self.ps_attrs_getter(path, data) else: raise NotImplementedError(f"No such field for {event.path}")
def __init__(self, **kwargs): super().__init__(**kwargs) self.counter = 0 self.color = RGB(white=True) self.r_phi = Modifier('red shift', 0.0, minimum=0, maximum=pi) self.b_phi = Modifier('blue shift', 35.0, minimum=0, maximum=pi) self.g_phi = Modifier('green shift', 70.0, minimum=0, maximum=pi) self.max_range = Modifier('max range', 1, minimum=1, maximum=self.strip_length) self.pattern_name = "Rainbow" self.modifiers = dict( r_phi=self.r_phi, b_phi=self.b_phi, g_phi=self.g_phi, max_range=self.max_range, )
def __init__(self, **kwargs): super().__init__(**kwargs) # min space between trail end and trail start self.centers = Modifier('centers', 10, minimum=self.strip_length // 2, maximum=self.strip_length) self.trail = Modifier('trail size', 3, minimum=1, maximum=self.strip_length // 2) self.counter = 0 self.pattern_name = "Snow" self.color = RGB(white=True) self.modifiers = dict( trail=self.trail, centers=self.centers, )
def process_rgba(self, path, data): """ Update RGBA values taking them from the database :return: """ # get the rgba attribute to update rgb_attr = path.split("/")[-1] # if is random if rgb_attr == "random": # update the randomize_color attribute of pattern if bool(data): color = RGB(random=True) self.pattern.update_args(color=color, randomize_color=True) else: color = self.get_rgba() self.pattern.update_args(color=color, randomize_color=False) else: # if is r,g,b,a, update just the value in the dictionary self.pattern.color.__dict__[rgb_attr] = int(data)
def fill(self): self.rate.value = 100 for idx in range(self.strip_length): self.pixels[idx]['color'] = RGB()
def fill(self): for idx in range(self.strip_length): self.pixels[idx]['color'] = RGB()
class Storm(Default): """ Steady color """ def __init__(self, **kwargs): super().__init__(**kwargs) self.pattern_name = "Storm" self.rain = Modifier('rain intensity', 10.0, minimum=0, maximum=0.2) self.thunder = Modifier('thunder intensity', 5.0, minimum=0, maximum=0.2) self.thunder_size = Modifier('thunder size', 80, minimum=0, maximum=self.strip_length) self.thunder_color = RGB(white=True) self.rain_color = RGB(r=70, g=100, b=255, a=255) self.thunder_centers = {} for idx in range(self.strip_length): self.pixels[idx]['rain'] = 0 def fill(self): def rain_handler(idx): rn = self.pixels[idx]['rain'] if not rn: rn = 255 * random() if random() < self.rain() else 0 else: rn = bound_sub(rn, rain_loss) self.pixels[idx]['rain'] = rn if rn: c = self.rain_color.copy() c.blend(self.pixels[idx]['color']) c.update_single(a=rn) self.pixels[idx]['color'] = c def thunder_handler(idx): """ Handle thunder :param idx: :return: """ # if current index is a thunder center if idx in self.thunder_centers.keys(): # get the values start, end, alpha = self.thunder_centers[idx] # mimic thunder behavior to brighten up with random value if random() < 0.1: alpha = bound_add(alpha, thunder_loss * 2) else: alpha = bound_sub(alpha, thunder_loss) # update alpha self.thunder_centers[idx][2] = alpha # for every part of the thunder for jdx in range(start, end + 1): # copy the thunder color c = self.thunder_color.copy() # estimate the distance from center as a loss + a random term l = 200 * abs(jdx - idx) / self.thunder_size() // 2 + randint(0, 50) # estimate new local alpha v = bound_sub(alpha, thunder_loss + l) # update in color c.update_single(a=v) # set # c.blend(self.pixels[jdx]['color']) self.pixels[jdx]['color'] = c # if alpha is zero then remove the center if alpha == 0: del self.thunder_centers[idx] thunder_loss = 30 rain_loss = 10 # if there are not enough thunders max_centers = self.strip_length // self.thunder_size() if len(self.thunder_centers) < max_centers and random() < self.thunder(): # get how many more do we need max_centers -= len(self.thunder_centers) for _ in range(max_centers): # if probability is high enough # estimate random center center = randint(0, self.strip_length) size = self.thunder_size() # get start and end start = center - size // 2 - randint(0, size // 4) start = start if start >= 0 else 0 end = size // 2 + center + randint(0, size // 4) end = end if end < self.strip_length else self.strip_length # add it to dictionary self.thunder_centers[center] = [start, end, 255] for i in range(self.strip_length): rain_handler(i) thunder_handler(i)
def empty_center(self): if self.randomize_color: return dict(color=RGB(random=True), tail=[], step=0) else: return dict(color=self.color, tail=[], step=0)
parser.add_argument('--strip_type', type=str, help='rate', default='neopixel', choices=['neopixel', 'dotstar']) parser.add_argument('--pixels', type=int, help='rate', default=300) parser.add_argument('--profile', nargs='?', const=True, default=False, help='If to start in debug mode') args = parser.parse_args() # set pattern, color and handler pat = Patterns[args.pattern] color = RGB(random=True) if args.strip_type == "neopixel": handler = NeoPixel(args.pixels) else: handler = Dotstar(args.pixels) # init app and run app = pat(handler=handler, rate=args.rate, pixels=args.pixels, color=color) # start yappi if profile flag if args.profile: yappi.start() try: # run while not stopped