~quf/xmastree2020

80afe8699e1e2a6d55761d14c285a44d6aa4739b — Lukas Himbert 3 years ago f4ceea4 main
a bad attempt at recreating the stand-up maths intro
1 files changed, 170 insertions(+), 0 deletions(-)

A intro.py
A intro.py => intro.py +170 -0
@@ 0,0 1,170 @@
# 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

import numpy

class Rectangle:
    def __init__(self, a, b, c, d, color):
        # (a, b, c, d) are the vertices in clockwise or anti-clockwise order (either is fine, but it needs to be consistent).
        self.vertices = (a, b, c, d)
        self.color = color

    def contains(self, x):
        # see https://stackoverflow.com/a/2763387
        a, b, c, d = self.vertices
        return 0 <= numpy.dot(x - a, b - a) <= numpy.dot(b - a, b - a) and \
               0 <= numpy.dot(x - a, d - a) <= numpy.dot(d - a, d - a)

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

    # brightness factor:
    # 0 = black
    # 1 = maximum brightness
    brightness_factor = 1.0

    # shift azimuths (rotate x, y) such that radians in [0, π] are facing the room (radian 3π/2 should be facing into the corner):
    # then, the x axis points to the right, and the y axis points toward the room / viewer
    azimuths = (- azimuths + 0.7) % (2*pi)
    xs = radii * numpy.sin(azimuths)
    ys = radii * numpy.cos(azimuths)

    n_rectangles = 9

    # colours in GRB order
    white = tuple(round(255 * brightness_factor) for _ in range(3))
    black = (0, 0, 0)
    colors = [
        # might want to change these colors
        (40, 40, 40), # dark gray?
        (40, 209, 40), # red
        (0, 128, 128), # teal
        (190, 150, 120), # light brown
        (255, 255, 128), # pastel yellow
    ]
    colors = [tuple(round(c * brightness_factor) for c in color) for color in colors]

    while True:
        rectangles = [Rectangle(numpy.array((i/n_rectangles, 0.0)), numpy.array(((i-1)/n_rectangles, 0.0)), numpy.array(((i-1)/n_rectangles, 0.0)), numpy.array((i/n_rectangles, 0.0)), color=colors[i % len(colors)]) for i in range(n_rectangles)]
        # reset lights
        for i in range(len(coords)):
            pixels[i] = black
        pixels.show()

        # wait a bit
        time.sleep(1.0)

        # phase 1: roll in white from the right
        speed = 2.0
        t0 = time.time()
        delta = 0
        while delta <= 0.5 * pi / speed:
            delta = time.time() - t0
            for (i, (φ, z)) in enumerate(zip(azimuths, zs)):
                if φ < delta * speed:
                    pixels[i] = white
            pixels.show()

        time.sleep(0.5)

        # phase 2: striped band grows out sideways
        t0 = time.time()
        delta = 0
        speed = 2
        w = 0.05
        while delta < w / speed:
            now = time.time()
            delta = now - t0
            for rect in rectangles:
                rect.vertices[0][1] = numpy.clip(-delta * speed, -w, 0)
                rect.vertices[1][1] = numpy.clip(-delta * speed, -w, 0)
                for (i, (x, y, z)) in enumerate(zip(xs, ys, zs)):
                    if rect.contains((z, y)) and x >= 0:
                        pixels[i] = rect.color
            pixels.show()
        for rect in rectangles:
            rect.vertices[0][1] = numpy.clip(-delta * speed, -w, 0)
            rect.vertices[1][1] = numpy.clip(-delta * speed, -w, 0)
            for (i, (x, y, z)) in enumerate(zip(xs, ys, zs)):
                if rect.contains((z, y)) and x >= 0:
                    pixels[i] = rect.color
        pixels.show()

        time.sleep(5)

    return 'DONE'


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