@@ 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()