~quf/xmastree2020

f4ceea420a6c971bd5c8bf5deb86bef45f22af83 — Lukas Himbert 3 years ago c786cb7
meandering spotlights
1 files changed, 180 insertions(+), 0 deletions(-)

A blending-spotlights.py
A blending-spotlights.py => blending-spotlights.py +180 -0
@@ 0,0 1,180 @@
# Try to import board and neopixel to run on the real hardware
try:
    import board
    import neopixel
except ImportError:
    # Error, try to import the simulation from DutChen18 instead (https://github.com/standupmaths/xmastree2020/pull/5)
    from sim import board, neopixel

class SpotLight:
    def __init__(self, size, color, z0, φ0, omega, z_speed):
        self.size = size
        self.color = color
        self.z = z0
        self.φ = φ0
        self.omega = omega
        self.z_speed = z_speed

def xmaslight():
    # This is the code from my 
    
    #NOTE THE LEDS ARE GRB COLOUR (NOT RGB)
    
    # Here are the libraries I am currently using:
    import time
    import re
    from math import sin, cos, atan2, pi, exp, floor
    #import board
    #import neopixel

    # You are welcome to add any of these:
    import random
    import numpy
    # import scipy
    # import sys
    
    # If you want to have user changable values, they need to be entered from the command line
    # so import sys sys and use sys.argv[0] etc
    # some_value = int(sys.argv[0])
    
    # IMPORT THE COORDINATES (please don't break this bit)
    
    coordfilename = "Python/coords.txt"
	
    fin = open(coordfilename,'r')
    coords_raw = fin.readlines()
    
    coords_bits = [i.split(",") for i in coords_raw]
    
    coords = []
    
    for slab in coords_bits:
        new_coord = []
        for i in slab:
            new_coord.append(int(re.sub(r'[^-\d]','', i)))
        coords.append(new_coord)
    
    #set up the pixels (AKA 'LEDs')
    PIXEL_COUNT = len(coords) # this should be 500
    
    pixels = neopixel.NeoPixel(board.D18, PIXEL_COUNT, auto_write=False)
    
    
    # YOU CAN EDIT FROM HERE DOWN

    # unzip coordinates
    xs, ys, zs = list(zip(*coords))
    xs = numpy.array(xs)
    ys = numpy.array(ys)
    zs = numpy.array(zs)

    # normalize heights (0 to 1)
    zs = (zs - zs.min()) / (zs.max() - zs.min())

    # Find center of the tree
    x0 = numpy.average([x for (x, _, _) in coords])
    y0 = numpy.average([y for (_, y, _) in coords])

    # normalized radii (0 to 1)
    radii = numpy.sqrt((xs - x0)**2, (ys - y0)**2)
    radii = radii / radii.max()

    # azimuths
    azimuths = numpy.arctan2(ys, xs)

    # VARIOUS SETTINGS

    # rotation speed in radians per second
    min_omega = pi/8
    max_omega = pi/2
    # z speed
    min_z_speed = 0.05
    max_z_speed = 0.4

    # speed half-life in seconds
    speed_halflife = 1

    # afterglow
    afterglow_halflife = 0.5

    randfloat = lambda min, max: (min + (max - min) * random.random()) * (-1)**(random.randint(0, 1))

    # colours in GRB order
    white = (255, 255, 255)
    black = (0, 0, 0)
    spotlights = [
        SpotLight(
            size = 0.15,
            color = (255, 0, 0),
            z0 = random.random(),
            φ0 = 2*pi*random.random(),
            omega = randfloat(min_omega, max_omega),
            z_speed = randfloat(min_z_speed, max_z_speed),
        ),
        SpotLight(
            size = 0.10,
            color = (0, 255, 0),
            z0 = random.random(),
            φ0 = 2*pi*random.random(),
            omega = randfloat(min_omega, max_omega),
            z_speed = randfloat(min_z_speed, max_z_speed),
        ),
        SpotLight(
            size = 0.15,
            color = (0, 0, 255),
            z0 = random.random(),
            φ0 = 2*pi*random.random(),
            omega = randfloat(min_omega, max_omega),
            z_speed = randfloat(min_z_speed, max_z_speed),
        ),
    ]
    
    # INITIALISE SOME VALUES
    t0 = time.time()

    colors = len(coords) * [black]

    while True:
        # update spotlights
        t1 = time.time()
        delta = t1 - t0
        t0 = t1

        # fade out lights
        for i in range(len(coords)):
            colors[i] = tuple(floor(component * 0.5**(delta / afterglow_halflife)) for component in colors[i])

        # update spotlights
        for light in spotlights:
            light.φ += delta * light.omega
            light.z += delta * light.z_speed

            # make sure it's between the two ends of the tree
            # if not, bounce back
            if light.z < light.size or light.z + light.size > 1:
                light.z = numpy.clip(light.z, light.size, 1 - light.size)
                light.z_speed = - light.z_speed

            # after a random delay (exponentially distributed), change direction
            if random.random() > 0.5 ** (delta / speed_halflife):
                light.z_speed = randfloat(min_z_speed, max_z_speed)
                light.omega = randfloat(min_omega, max_omega)

        # Turn the lights on if they are in the spotlight
        for (i, (φ, z)) in enumerate(zip(azimuths, zs)):
            for light in spotlights:
                if abs(φ - light.φ) % (2*pi) <= 2*pi*light.size * light.z and abs(z - light.z) <= light.size:
                    colors[i] = tuple(numpy.clip(colors[i][j] + light.color[j], 0, 255) for j in range(3))

        for (i, col) in enumerate(colors):
            pixels[i] = col

        # use the show() option as rarely as possible as it takes ages
        # do not use show() each time you change a LED but rather wait until you have changed them all
        pixels.show()
        
    return 'DONE'


# yes, I just put this at the bottom so it auto runs
xmaslight()