From 905bc26e32375a7de1eac7accaf6dd47ddecf17e Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Thu, 24 Dec 2020 13:24:36 +0100 Subject: [PATCH 01/10] major refactoring + Virtual Tree --- Python/coords.txt | 500 +++++++++++++++++++++++++++++++++++++++++++++ xmaslights-spin.py | 216 ++++++++++++-------- 2 files changed, 630 insertions(+), 86 deletions(-) create mode 100644 Python/coords.txt diff --git a/Python/coords.txt b/Python/coords.txt new file mode 100644 index 0000000..747a37f --- /dev/null +++ b/Python/coords.txt @@ -0,0 +1,500 @@ +[-61, -39, -336] +[-58, -94, -372] +[-112, -121, -394] +[-114, -149, -401] +[-122, -126, -348] +[-165, -180, -355] +[-186, -155, -319] +[-228, -157, -326] +[-216, -102, -328] +[-250, -102, -354] +[-250, -62, -360] +[-201, -19, -381] +[-232, 23, -402] +[-195, 26, -329] +[-206, 34, -434] +[-169, 70, -336] +[-151, 93, -379] +[-145, 73, -377] +[-131, 73, -357] +[-114, 86, -294] +[-80, 58, -280] +[-62, 66, -239] +[-16, 85, -257] +[-47, 122, -289] +[-49, 140, -256] +[-21, 163, -285] +[-2, 157, -277] +[16, 120, -272] +[47, 144, -363] +[50, 148, -356] +[89, 159, -350] +[106, 113, -381] +[100, 91, -323] +[108, 99, -364] +[135, 46, -430] +[95, 29, -365] +[118, 24, -356] +[99, -3, -320] +[143, 6, -322] +[139, -27, -262] +[139, -20, -321] +[164, 22, -293] +[192, -9, -316] +[243, -56, -364] +[227, -84, -334] +[190, -83, -418] +[185, -70, -427] +[152, -70, -454] +[138, -90, -416] +[107, -44, -416] +[37, -34, -341] +[16, -66, -389] +[-21, -114, -390] +[-26, -60, -401] +[-44, -48, -357] +[-67, -104, -394] +[-27, -111, -359] +[2, -86, -332] +[-12, -92, -364] +[25, -128, -384] +[49, -101, -376] +[94, -116, -322] +[110, -72, -300] +[75, -81, -331] +[49, -112, -356] +[95, -151, -358] +[120, -169, -350] +[93, -125, -297] +[164, -148, -276] +[175, -164, -313] +[161, -164, -340] +[114, -198, -346] +[110, -201, -356] +[78, -186, -353] +[46, -158, -316] +[31, -134, -229] +[20, -126, -298] +[10, -112, -250] +[10, -72, -250] +[9, -59, -214] +[-46, -40, -240] +[-26, -102, -274] +[-33, -135, -259] +[-36, -154, -266] +[-27, -191, -253] +[21, -173, -213] +[-3, -193, -178] +[67, -223, -269] +[67, -230, -276] +[13, -200, -267] +[27, -218, -398] +[-7, -211, -414] +[-9, -188, -438] +[-44, -212, -434] +[-52, -157, -410] +[-104, -209, -369] +[-121, -206, -342] +[-133, -148, -278] +[-124, -209, -270] +[-85, -170, -251] +[-132, -191, -233] +[-97, -215, -240] +[-120, -164, -266] +[-60, -102, -260] +[-127, -103, -240] +[-115, -102, -216] +[-129, -85, -213] +[-119, -64, -239] +[-157, -48, -306] +[-160, 18, -260] +[-110, 8, -280] +[-130, 48, -290] +[-62, 44, -272] +[-110, 48, -280] +[-105, 63, -252] +[-178, 46, -294] +[-209, 62, -231] +[-187, -4, -323] +[-232, 18, -352] +[-209, 68, -230] +[-234, 110, -222] +[-205, 123, -254] +[-238, 135, -302] +[-217, 162, -324] +[-193, 132, -350] +[-209, 164, -376] +[-186, 144, -418] +[-120, 138, -390] +[-108, 168, -362] +[-78, 152, -320] +[-103, 198, -286] +[-90, 174, -243] +[-64, 200, -248] +[-106, 225, -293] +[-107, 171, -324] +[-93, 157, -270] +[-65, 200, -240] +[-33, 186, -289] +[-78, 229, -302] +[-95, 239, -288] +[-94, 224, -276] +[-48, 189, -219] +[-57, 200, -174] +[-9, 185, -222] +[-10, 138, -189] +[6, 95, -185] +[24, 79, -165] +[49, 108, -218] +[28, 82, -198] +[24, 64, -183] +[-12, 94, -228] +[-41, 159, -234] +[-24, 102, -166] +[-49, 136, -125] +[-14, 96, -109] +[-21, 109, -101] +[-25, 120, -120] +[-4, 163, -130] +[-4, 145, -148] +[2, 163, -175] +[12, 209, -210] +[-21, 200, -164] +[-33, 252, -198] +[-83, 239, -234] +[-109, 222, -194] +[-79, 198, -177] +[-110, 229, -196] +[-73, 194, -258] +[-123, 192, -278] +[-103, 159, -282] +[-145, 171, -292] +[-157, 159, -231] +[-114, 178, -189] +[-102, 160, -176] +[-102, 169, -162] +[-125, 153, -102] +[-167, 176, -100] +[-139, 138, -40] +[-144, 100, -69] +[-127, 52, -86] +[-131, 48, -113] +[-140, 41, -143] +[-160, 27, -135] +[-140, 49, -81] +[-164, 23, -120] +[-160, 26, -84] +[-194, 90, -79] +[-185, 81, -59] +[-233, 110, -106] +[-202, 115, -136] +[-231, 96, -168] +[-185, 80, -218] +[-214, 53, -180] +[-193, 56, -168] +[-174, 15, -186] +[-179, 48, -148] +[-164, 14, -123] +[-131, 52, -81] +[-100, 30, -100] +[-116, -14, -108] +[-189, -13, -175] +[-217, -37, -173] +[-191, -90, -170] +[-227, -79, -271] +[-174, -79, -216] +[-224, -94, -302] +[-180, -92, -306] +[-168, -66, -300] +[-160, -40, -289] +[-151, -72, -256] +[-147, -58, -198] +[-134, -52, -191] +[-128, -40, -173] +[-160, -60, -158] +[-131, -41, -117] +[-130, -40, -87] +[-101, -27, -68] +[-120, 19, -39] +[-127, 20, -20] +[-99, 8, 3] +[-116, 20, 56] +[-116, 35, 76] +[-58, 36, 75] +[-99, 29, 79] +[-97, 4, 68] +[-120, -43, 123] +[-169, -37, 120] +[-149, -36, 91] +[-107, -46, 42] +[-104, -20, -4] +[-96, -12, -45] +[-89, -24, -76] +[-76, -29, -64] +[-116, -2, -76] +[-77, -38, -57] +[-109, 2, -62] +[-80, -24, -64] +[-112, -37, -14] +[-116, -47, -22] +[-99, -68, 2] +[-141, -113, 49] +[-182, -171, 6] +[-191, -153, -32] +[-191, -157, -84] +[-160, -184, -130] +[-182, -156, -158] +[-156, -160, -188] +[-182, -149, -211] +[-125, -139, -202] +[-141, -158, -232] +[-77, -168, -161] +[-110, -192, -239] +[-100, -198, -172] +[-109, -211, -146] +[-70, -207, -150] +[-105, -209, -114] +[-31, -176, -143] +[-33, -212, -145] +[-58, -171, -129] +[-130, -62, -110] +[-38, -180, -134] +[-31, -182, -145] +[-52, -191, -122] +[-34, -190, -104] +[-88, -152, -54] +[-27, -165, -36] +[-77, -127, 4] +[-74, -162, 35] +[-47, -110, 48] +[-26, -145, 79] +[-48, -116, 90] +[-62, -130, 133] +[-57, -134, 80] +[-84, -157, 48] +[-67, -165, 41] +[-37, -129, -29] +[-16, -125, -57] +[23, -90, -38] +[-17, -112, -57] +[11, -109, -35] +[20, -155, -47] +[54, -170, -70] +[60, -181, -81] +[53, -159, -148] +[50, -160, -153] +[101, -176, -173] +[42, -176, -188] +[80, -161, -254] +[60, -194, -314] +[76, -191, -372] +[34, -166, -403] +[79, -183, -374] +[93, -175, -367] +[90, -205, -398] +[108, -224, -346] +[93, -199, -350] +[152, -197, -359] +[204, -252, -398] +[209, -249, -414] +[176, -202, -440] +[117, -156, -327] +[142, -162, -295] +[109, -62, -294] +[105, -115, -270] +[70, -135, -243] +[70, -106, -244] +[82, -83, -232] +[61, -106, -196] +[91, -83, -194] +[83, -93, -128] +[88, -94, -88] +[57, -111, -103] +[51, -95, -146] +[76, -162, -138] +[111, -146, -124] +[65, -132, -104] +[66, -128, -43] +[38, -89, -48] +[48, -94, -33] +[72, -101, 29] +[47, -97, 65] +[70, -124, 38] +[117, -137, 24] +[105, -112, -28] +[142, -75, -56] +[125, -72, -56] +[167, -84, -100] +[161, -51, -132] +[208, -82, -158] +[181, -83, -192] +[176, -79, -174] +[249, -118, -222] +[204, -56, -284] +[205, -48, -282] +[203, -32, -240] +[180, -67, -255] +[189, -31, -218] +[176, -28, -192] +[157, -72, -196] +[106, -21, -202] +[94, -25, -208] +[67, -49, -167] +[66, -37, -130] +[79, -44, -162] +[75, 8, -192] +[119, -25, -251] +[161, -41, -194] +[116, -4, -210] +[131, 7, -153] +[141, -37, -205] +[124, 10, -172] +[126, 33, -147] +[152, 50, -139] +[151, 14, -153] +[140, 18, -90] +[159, 44, -98] +[150, 94, -74] +[157, 73, -141] +[142, 91, -143] +[162, 119, -168] +[174, 73, -218] +[166, 125, -239] +[187, 138, -266] +[192, 88, -305] +[237, 154, -326] +[180, 141, -330] +[172, 183, -302] +[157, 118, -220] +[149, 167, -242] +[180, 184, -215] +[150, 216, -210] +[90, 179, -167] +[92, 160, -183] +[146, 237, -226] +[154, 253, -281] +[108, 206, -216] +[170, 224, -317] +[202, 225, -325] +[185, 168, -350] +[209, 167, -321] +[165, 110, -326] +[222, 67, -213] +[224, 85, -282] +[150, 53, -223] +[185, 30, -156] +[192, 43, -182] +[185, 0, -145] +[149, 4, -128] +[131, -7, -139] +[115, -2, -139] +[95, -3, -129] +[100, 27, -61] +[93, 24, -58] +[114, -23, -14] +[122, -12, -7] +[101, -21, 60] +[83, 20, 75] +[51, 11, 91] +[64, 55, 89] +[62, 77, 114] +[48, 134, 88] +[54, 135, 24] +[36, 152, 8] +[26, 89, 0] +[14, 120, -30] +[-22, 120, -22] +[-43, 139, -19] +[-22, 129, 20] +[-91, 152, 32] +[-112, 142, 42] +[-119, 158, 51] +[-112, 96, 74] +[-125, 88, 119] +[-95, 57, 119] +[-85, 84, 133] +[-43, 45, 93] +[-6, 85, 79] +[30, 90, 90] +[52, 91, 99] +[69, 53, 98] +[100, 30, 94] +[95, 20, 87] +[117, -3, 111] +[100, 4, 111] +[99, 11, 164] +[72, -24, 166] +[86, -14, 170] +[45, -25, 164] +[17, 22, 137] +[-9, 15, 144] +[-18, 17, 149] +[-21, -17, 137] +[-68, -34, 184] +[-22, -82, 141] +[-44, -72, 144] +[-66, -150, 157] +[-70, -151, 157] +[-39, -159, 137] +[-3, -157, 113] +[-10, -143, 87] +[24, -133, 74] +[55, -131, 71] +[32, -130, 109] +[88, -100, 88] +[48, -59, 118] +[71, -84, 125] +[23, -79, 153] +[28, -22, 138] +[43, -59, 174] +[39, -44, 209] +[-66, -14, 192] +[-105, 10, 229] +[-94, 70, 252] +[-122, 25, 276] +[-81, 51, 215] +[-62, 82, 209] +[-24, 72, 196] +[-21, 63, 183] +[-24, 72, 176] +[62, 92, 190] +[68, 108, 246] +[58, 64, 186] +[114, 66, 212] +[65, 1, 208] +[79, 20, 239] +[91, 14, 252] +[79, -22, 266] +[92, -12, 293] +[71, -36, 316] +[79, -19, 332] +[78, -64, 399] +[21, -32, 323] +[14, -18, 324] +[-11, -14, 359] +[-13, 0, 393] +[-4, 0, 406] +[48, 30, 423] +[37, -4, 414] +[31, -50, 453] +[-6, -21, 446] +[-11, 4, 430] +[9, 23, 400] +[-30, 54, 370] +[-36, 45, 325] +[-35, 38, 310] +[-43, 34, 277] +[7, 35, 231] +[-40, -26, 244] +[-24, -50, 282] +[-57, -49, 294] +[-6, -44, 233] +[0, -95, 282] +[48, -76, 234] +[48, -114, 288] +[19, -88, 255] +[70, -97, 242] +[68, -94, 223] +[46, -144, 170] +[18, -132, 172] +[48, -128, 142] \ No newline at end of file diff --git a/xmaslights-spin.py b/xmaslights-spin.py index 3b77d79..e628ccf 100644 --- a/xmaslights-spin.py +++ b/xmaslights-spin.py @@ -1,58 +1,102 @@ -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 board - import neopixel - import re - import math - - # 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 - - # I get a list of the heights which is not overly useful here other than to set the max and min altitudes - heights = [] - for i in coords: - heights.append(i[2]) - - min_alt = min(heights) - max_alt = max(heights) - +# Here are the libraries I am currently using: +import time +# import board +# import neopixel # pip install rpi_ws281x adafruit-circuitpython-neopixel adafruit-blinka +import math +from collections import namedtuple # for Point data structure +import pygame # for virtual tree visuals + + +# 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]) + + +# class XmasTree: +# def __init__(self, coord_filename): +# self.coords = self.read_coordinates(coord_filename) +# self.min_x = min(coord.x for coord in self.coords) +# self.max_x = max(coord.x for coord in self.coords) +# self.min_y = min(coord.y for coord in self.coords) +# self.max_y = max(coord.y for coord in self.coords) +# self.min_z = min(coord.z for coord in self.coords) +# self.max_z = max(coord.z for coord in self.coords) +# +# self.PIXEL_COUNT = len(self.coords) +# assert (self.PIXEL_COUNT == 500) +# self.pixels = neopixel.NeoPixel(board.D18, self.PIXEL_COUNT, auto_write=False) +# +# def read_coordinates(self, coord_filename): +# Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans +# with open(coord_filename, 'r') as fin: +# coords_raw = fin.readlines() +# return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) +# +# def display(self): +# self.pixels.show() +# +# def set_led_RGB(self, n, RGBcolor): +# self.pixels[n] = [RGBcolor[1], RGBcolor[0], RGBcolor[2]] + + +class VirtualXmasTree: + def __init__(self, coord_filename): + self.coords = self.read_coordinates(coord_filename) + self.min_x = min(coord.x for coord in self.coords) + self.max_x = max(coord.x for coord in self.coords) + self.min_y = min(coord.y for coord in self.coords) + self.max_y = max(coord.y for coord in self.coords) + self.min_z = min(coord.z for coord in self.coords) + self.max_z = max(coord.z for coord in self.coords) + + self.PIXEL_COUNT = len(self.coords) + assert (self.PIXEL_COUNT == 500) + self.pixels = [[0, 0, 0]] * self.PIXEL_COUNT + + self.tree_size = self.max_z - self.min_z + self.max_light_size = 10 + self.min_light_size = 4 + self.margin = self.tree_size * 0.1 + self.WINDOW_WIDTH, self.WINDOW_HEIGHT = 800, 800 + pygame.init() + self.clock = pygame.time.Clock() + self.screen = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT)) + pygame.display.set_caption("Virtual Xmas Tree") + + def read_coordinates(self, coord_filename): + Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans + with open(coord_filename, 'r') as fin: + coords_raw = fin.readlines() + return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) + + def display(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + exit(0) + + self.screen.fill((127, 127, 127)) + + for p in range(len(self.coords)): + screen_x = int(self.margin + self.WINDOW_WIDTH * ((self.coords[p].x - self.min_x) / self.tree_size)) + screen_y = int(self.WINDOW_HEIGHT - self.margin - self.WINDOW_HEIGHT * ((self.coords[p].z - self.min_z) / self.tree_size)) + on_screen_size = int(self.min_light_size + (self.coords[p].y - self.min_y) / (self.max_y - self.min_y) * (self.max_light_size - self.min_light_size)) + pygame.draw.circle(self.screen, self.pixels[p], (screen_x, screen_y), on_screen_size) + + pygame.display.flip() + self.clock.tick(60) + + def set_led_RGB(self, n, RGBcolor): + self.pixels[n] = RGBcolor + + +def xmaslight(tree): + # VARIOUS SETTINGS # how much the rotation points moves each time @@ -64,18 +108,16 @@ def xmaslight(): # pause between cycles (normally zero as it is already quite slow) slow = 0 - # startin angle (in radians) + # starting angle (in radians) angle = 0 # how much the angle changes per cycle inc = 0.1 - # the two colours in GRB order - # if you are turning a lot of them on at once, keep their brightness down please - colourA = [0,50,50] # purple - colourB = [50,50,0] # yellow - - + # if you are turning a lot of lights on at once, keep their brightness down please + colourA = [50, 0, 50] # purple + colourB = [50, 50, 0] # yellow + # INITIALISE SOME VALUES swap01 = 0 @@ -87,23 +129,20 @@ def xmaslight(): # the starting point on the vertical axis c = 100 - # yes, I just run which run is true - run = 1 - while run == 1: - + # run forever + while True: + time.sleep(slow) - - LED = 0 - while LED < len(coords): - if math.tan(angle)*coords[LED][1] <= coords[LED][2]+c: - pixels[LED] = colourA + + for led in range(len(tree.coords)): + if math.tan(angle) * tree.coords[led].y <= tree.coords[led].z + c: + tree.set_led_RGB(led, colourA) else: - pixels[LED] = colourB - LED += 1 - - # 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() + tree.set_led_RGB(led, colourB) + + # use the display() option as rarely as possible as it takes ages + # do not use display() each time you change a LED but rather wait until you have changed them all + tree.display() # now we get ready for the next cycle @@ -118,27 +157,32 @@ def xmaslight(): if angle >= 0.5*math.pi: if swap01 == 0: colour_hold = [i for i in colourA] - colourA =[i for i in colourB] + colourA = [i for i in colourB] colourB = [i for i in colour_hold] swap01 = 1 if angle >= 1.5*math.pi: if swap02 == 0: colour_hold = [i for i in colourA] - colourA =[i for i in colourB] + colourA = [i for i in colourB] colourB = [i for i in colour_hold] swap02 = 1 # and we move the rotation point - c += direction*dinc + c += direction * dinc - if c <= min_alt+buffer: + if c <= tree.min_z + buffer: direction = 1 - if c >= max_alt-buffer: + if c >= tree.max_z - buffer: direction = -1 - - return 'DONE' -# yes, I just put this at the bottom so it auto runs -xmaslight() +def run_lights(): + coord_filename = "Python/coords.txt" + # tree = XmasTree(coord_filename) + tree = VirtualXmasTree(coord_filename) + xmaslight(tree) # we pass the display to the function and let it run + + +if __name__ == "__main__": + run_lights() From a5224c97f27e7508f10ec1f0cbaa66fe9d655090 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Thu, 24 Dec 2020 14:58:06 +0100 Subject: [PATCH 02/10] spirolight function --- xmaslights-spin.py | 115 ++++++++++++--------------------------------- xmastrees.py | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 85 deletions(-) create mode 100644 xmastrees.py diff --git a/xmaslights-spin.py b/xmaslights-spin.py index e628ccf..54c18a6 100644 --- a/xmaslights-spin.py +++ b/xmaslights-spin.py @@ -1,11 +1,8 @@ -# Here are the libraries I am currently using: +# refactored by pikuch 24.12.2020 + import time -# import board -# import neopixel # pip install rpi_ws281x adafruit-circuitpython-neopixel adafruit-blinka import math -from collections import namedtuple # for Point data structure -import pygame # for virtual tree visuals - +from xmastrees import VirtualXmasTree, XmasTree # You are welcome to add any of these: # import random @@ -18,83 +15,7 @@ # some_value = int(sys.argv[0]) -# class XmasTree: -# def __init__(self, coord_filename): -# self.coords = self.read_coordinates(coord_filename) -# self.min_x = min(coord.x for coord in self.coords) -# self.max_x = max(coord.x for coord in self.coords) -# self.min_y = min(coord.y for coord in self.coords) -# self.max_y = max(coord.y for coord in self.coords) -# self.min_z = min(coord.z for coord in self.coords) -# self.max_z = max(coord.z for coord in self.coords) -# -# self.PIXEL_COUNT = len(self.coords) -# assert (self.PIXEL_COUNT == 500) -# self.pixels = neopixel.NeoPixel(board.D18, self.PIXEL_COUNT, auto_write=False) -# -# def read_coordinates(self, coord_filename): -# Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans -# with open(coord_filename, 'r') as fin: -# coords_raw = fin.readlines() -# return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) -# -# def display(self): -# self.pixels.show() -# -# def set_led_RGB(self, n, RGBcolor): -# self.pixels[n] = [RGBcolor[1], RGBcolor[0], RGBcolor[2]] - - -class VirtualXmasTree: - def __init__(self, coord_filename): - self.coords = self.read_coordinates(coord_filename) - self.min_x = min(coord.x for coord in self.coords) - self.max_x = max(coord.x for coord in self.coords) - self.min_y = min(coord.y for coord in self.coords) - self.max_y = max(coord.y for coord in self.coords) - self.min_z = min(coord.z for coord in self.coords) - self.max_z = max(coord.z for coord in self.coords) - - self.PIXEL_COUNT = len(self.coords) - assert (self.PIXEL_COUNT == 500) - self.pixels = [[0, 0, 0]] * self.PIXEL_COUNT - - self.tree_size = self.max_z - self.min_z - self.max_light_size = 10 - self.min_light_size = 4 - self.margin = self.tree_size * 0.1 - self.WINDOW_WIDTH, self.WINDOW_HEIGHT = 800, 800 - pygame.init() - self.clock = pygame.time.Clock() - self.screen = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT)) - pygame.display.set_caption("Virtual Xmas Tree") - - def read_coordinates(self, coord_filename): - Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans - with open(coord_filename, 'r') as fin: - coords_raw = fin.readlines() - return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) - - def display(self): - for event in pygame.event.get(): - if event.type == pygame.QUIT: - exit(0) - - self.screen.fill((127, 127, 127)) - - for p in range(len(self.coords)): - screen_x = int(self.margin + self.WINDOW_WIDTH * ((self.coords[p].x - self.min_x) / self.tree_size)) - screen_y = int(self.WINDOW_HEIGHT - self.margin - self.WINDOW_HEIGHT * ((self.coords[p].z - self.min_z) / self.tree_size)) - on_screen_size = int(self.min_light_size + (self.coords[p].y - self.min_y) / (self.max_y - self.min_y) * (self.max_light_size - self.min_light_size)) - pygame.draw.circle(self.screen, self.pixels[p], (screen_x, screen_y), on_screen_size) - - pygame.display.flip() - self.clock.tick(60) - - def set_led_RGB(self, n, RGBcolor): - self.pixels[n] = RGBcolor - - +# original function def xmaslight(tree): # VARIOUS SETTINGS @@ -177,11 +98,35 @@ def xmaslight(tree): direction = -1 +def spirolight(tree): + # starting angle for each color channel (in radians) + angles = [0.0] * 3 + # how much the angle changes per cycle + delta_angles = [1/5, 1/7, 1/9] + # maximum intensity of any one colour channel + max_intensity = 100 + + while True: + for led in range(len(tree.coords)): + rotation = math.atan2(tree.coords[led].y, tree.coords[led].x) + colour = [max_intensity * (math.sin(angles[0] + rotation) + 1) / 2, + max_intensity * (math.sin(angles[1] + rotation) + 1) / 2, + max_intensity * (math.sin(angles[2] + rotation) + 1) / 2] + tree.set_led_RGB(led, colour) + + tree.display() + + # update angles + for i in range(len(angles)): + angles[i] = math.fmod(angles[i] + delta_angles[i], 2 * math.pi) + + def run_lights(): coord_filename = "Python/coords.txt" - # tree = XmasTree(coord_filename) tree = VirtualXmasTree(coord_filename) - xmaslight(tree) # we pass the display to the function and let it run + spirolight(tree) + # tree = XmasTree(coord_filename) + # xmaslight(tree) if __name__ == "__main__": diff --git a/xmastrees.py b/xmastrees.py new file mode 100644 index 0000000..dba02f1 --- /dev/null +++ b/xmastrees.py @@ -0,0 +1,84 @@ +from collections import namedtuple # for Point data structure +import pygame # for virtual tree visuals + + +class XmasTree: + def __init__(self, coord_filename): + + import board + import neopixel # pip install rpi_ws281x adafruit-circuitpython-neopixel adafruit-blinka + + self.coords = self.read_coordinates(coord_filename) + self.min_x = min(coord.x for coord in self.coords) + self.max_x = max(coord.x for coord in self.coords) + self.min_y = min(coord.y for coord in self.coords) + self.max_y = max(coord.y for coord in self.coords) + self.min_z = min(coord.z for coord in self.coords) + self.max_z = max(coord.z for coord in self.coords) + + self.PIXEL_COUNT = len(self.coords) + assert (self.PIXEL_COUNT == 500) + self.pixels = neopixel.NeoPixel(board.D18, self.PIXEL_COUNT, auto_write=False) + + def read_coordinates(self, coord_filename): + Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans + with open(coord_filename, 'r') as fin: + coords_raw = fin.readlines() + return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) + + def display(self): + self.pixels.show() + + def set_led_RGB(self, n, RGBcolor): + self.pixels[n] = [RGBcolor[1], RGBcolor[0], RGBcolor[2]] + + +class VirtualXmasTree: + def __init__(self, coord_filename): + self.coords = self.read_coordinates(coord_filename) + self.min_x = min(coord.x for coord in self.coords) + self.max_x = max(coord.x for coord in self.coords) + self.min_y = min(coord.y for coord in self.coords) + self.max_y = max(coord.y for coord in self.coords) + self.min_z = min(coord.z for coord in self.coords) + self.max_z = max(coord.z for coord in self.coords) + + self.PIXEL_COUNT = len(self.coords) + assert (self.PIXEL_COUNT == 500) + self.pixels = [[0, 0, 0]] * self.PIXEL_COUNT + + self.tree_size = self.max_z - self.min_z + self.max_light_size = 10 + self.min_light_size = 4 + self.margin = self.tree_size * 0.1 + self.WINDOW_WIDTH, self.WINDOW_HEIGHT = 800, 800 + pygame.init() + self.clock = pygame.time.Clock() + self.screen = pygame.display.set_mode((self.WINDOW_WIDTH, self.WINDOW_HEIGHT)) + pygame.display.set_caption("Virtual Xmas Tree") + + def read_coordinates(self, coord_filename): + Point = namedtuple("Point", ["x", "y", "z"]) # so we can address point coordinates like humans + with open(coord_filename, 'r') as fin: + coords_raw = fin.readlines() + return list(map(lambda line: Point(*map(lambda item: int(item.strip()), line[1:-2].split(","))), coords_raw)) + + def display(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + exit(0) + + self.screen.fill((10, 10, 10)) # background color + + for p in range(len(self.coords)): + # x is depth, y is x and z is height + screen_x = int(self.margin + self.WINDOW_WIDTH * ((self.coords[p].y - self.min_y) / self.tree_size)) + screen_y = int(self.WINDOW_HEIGHT - self.margin - self.WINDOW_HEIGHT * ((self.coords[p].z - self.min_z) / self.tree_size)) + on_screen_size = int(self.min_light_size + (self.coords[p].x - self.min_x) / (self.max_x - self.min_x) * (self.max_light_size - self.min_light_size)) + pygame.draw.circle(self.screen, self.pixels[p], (screen_x, screen_y), on_screen_size) + + pygame.display.flip() + self.clock.tick(60) + + def set_led_RGB(self, n, RGBcolor): + self.pixels[n] = RGBcolor From 1c9e3bae02134e6093ba69fb5de3314fba7c5d6e Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Thu, 24 Dec 2020 15:06:51 +0100 Subject: [PATCH 03/10] refactored to split user code --- spirolight.py | 25 ++++++++++++++++++ treeshow.py | 15 +++++++++++ xmaslights-spin.py => xmaslights.py | 41 +---------------------------- 3 files changed, 41 insertions(+), 40 deletions(-) create mode 100644 spirolight.py create mode 100644 treeshow.py rename xmaslights-spin.py => xmaslights.py (67%) diff --git a/spirolight.py b/spirolight.py new file mode 100644 index 0000000..f74d0bb --- /dev/null +++ b/spirolight.py @@ -0,0 +1,25 @@ +import math + + +# function by pikuch +def spirolight(tree): + # starting angle for each color channel (in radians) + angles = [0.0] * 3 + # how much the angle changes per cycle + delta_angles = [1/5, 1/7, 1/9] + # maximum intensity of any one colour channel + max_intensity = 100 + + while True: + for led in range(len(tree.coords)): + rotation = math.atan2(tree.coords[led].y, tree.coords[led].x) + colour = [max_intensity * (math.sin(angles[0] + rotation) + 1) / 2, + max_intensity * (math.sin(angles[1] + rotation) + 1) / 2, + max_intensity * (math.sin(angles[2] + rotation) + 1) / 2] + tree.set_led_RGB(led, colour) + + tree.display() + + # update angles + for i in range(len(angles)): + angles[i] = math.fmod(angles[i] + delta_angles[i], 2 * math.pi) diff --git a/treeshow.py b/treeshow.py new file mode 100644 index 0000000..e39fbf9 --- /dev/null +++ b/treeshow.py @@ -0,0 +1,15 @@ +from xmastrees import VirtualXmasTree, XmasTree +from spirolight import spirolight +from xmaslights import xmaslights + + +def run_lights(): + coord_filename = "Python/coords.txt" + # tree = XmasTree(coord_filename) + tree = VirtualXmasTree(coord_filename) + spirolight(tree) + # xmaslights(tree) + + +if __name__ == "__main__": + run_lights() diff --git a/xmaslights-spin.py b/xmaslights.py similarity index 67% rename from xmaslights-spin.py rename to xmaslights.py index 54c18a6..629a69e 100644 --- a/xmaslights-spin.py +++ b/xmaslights.py @@ -1,8 +1,5 @@ -# refactored by pikuch 24.12.2020 - import time import math -from xmastrees import VirtualXmasTree, XmasTree # You are welcome to add any of these: # import random @@ -15,8 +12,7 @@ # some_value = int(sys.argv[0]) -# original function -def xmaslight(tree): +def xmaslights(tree): # VARIOUS SETTINGS @@ -96,38 +92,3 @@ def xmaslight(tree): direction = 1 if c >= tree.max_z - buffer: direction = -1 - - -def spirolight(tree): - # starting angle for each color channel (in radians) - angles = [0.0] * 3 - # how much the angle changes per cycle - delta_angles = [1/5, 1/7, 1/9] - # maximum intensity of any one colour channel - max_intensity = 100 - - while True: - for led in range(len(tree.coords)): - rotation = math.atan2(tree.coords[led].y, tree.coords[led].x) - colour = [max_intensity * (math.sin(angles[0] + rotation) + 1) / 2, - max_intensity * (math.sin(angles[1] + rotation) + 1) / 2, - max_intensity * (math.sin(angles[2] + rotation) + 1) / 2] - tree.set_led_RGB(led, colour) - - tree.display() - - # update angles - for i in range(len(angles)): - angles[i] = math.fmod(angles[i] + delta_angles[i], 2 * math.pi) - - -def run_lights(): - coord_filename = "Python/coords.txt" - tree = VirtualXmasTree(coord_filename) - spirolight(tree) - # tree = XmasTree(coord_filename) - # xmaslight(tree) - - -if __name__ == "__main__": - run_lights() From 5177d16d9d56db92e63701c0fe81b6b2e69e45c9 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Thu, 24 Dec 2020 15:07:24 +0100 Subject: [PATCH 04/10] refactored to split user code --- treeshow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/treeshow.py b/treeshow.py index e39fbf9..125231d 100644 --- a/treeshow.py +++ b/treeshow.py @@ -1,4 +1,5 @@ from xmastrees import VirtualXmasTree, XmasTree +# user patterns from spirolight import spirolight from xmaslights import xmaslights From 0014d3ac1c3021cec13a606330c960cc8fc85f1a Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Fri, 25 Dec 2020 12:33:03 +0100 Subject: [PATCH 05/10] added wavesoflight function --- spirolight.py | 3 ++- treeshow.py | 5 ++++- wavesoflight.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 wavesoflight.py diff --git a/spirolight.py b/spirolight.py index f74d0bb..2bc440b 100644 --- a/spirolight.py +++ b/spirolight.py @@ -3,9 +3,10 @@ # function by pikuch def spirolight(tree): + """Draws three color waves (RGB) going around the tree at different speeds""" # starting angle for each color channel (in radians) angles = [0.0] * 3 - # how much the angle changes per cycle + # how quickly the color waves go around the tree delta_angles = [1/5, 1/7, 1/9] # maximum intensity of any one colour channel max_intensity = 100 diff --git a/treeshow.py b/treeshow.py index 125231d..18958fe 100644 --- a/treeshow.py +++ b/treeshow.py @@ -2,13 +2,16 @@ # user patterns from spirolight import spirolight from xmaslights import xmaslights - +from wavesoflight import wavesoflight def run_lights(): coord_filename = "Python/coords.txt" + # tree = XmasTree(coord_filename) tree = VirtualXmasTree(coord_filename) + spirolight(tree) + # wavesoflight(tree) # xmaslights(tree) diff --git a/wavesoflight.py b/wavesoflight.py new file mode 100644 index 0000000..dceb05a --- /dev/null +++ b/wavesoflight.py @@ -0,0 +1,48 @@ +import math +from random import randint + + +# function by pikuch +def wavesoflight(tree): + """Draws waves of light starting at random points""" + # current wave radius + radius = 0 + # maximum wave radius, after which it resets + max_radius = 800 + # the speed at which the wave spreads + wave_speed = 7 + # maximum intensity of any one colour channel + max_intensity = 100 + # wave source (led index) + source = randint(0, len(tree.coords)) + # led colors + leds = [[0, 0, 0]] * len(tree.coords) + # wave color + color = [randint(0, max_intensity), randint(0, max_intensity), randint(0, max_intensity)] + # color persistance (1 = stays forever, 0 = instantly off + persistance = 0.98 + + while True: + if radius > max_radius: + # create another wave + radius = 0 + source = randint(0, len(tree.coords)) + color = [randint(0, max_intensity), randint(0, max_intensity), randint(0, max_intensity)] + + radius += wave_speed + + # wave drawing + for led in range(len(tree.coords)): + distance_from_source = math.dist(tree.coords[led], tree.coords[source]) + if radius - wave_speed <= distance_from_source <= radius: + leds[led] = color + + # color dimming + for led in range(len(tree.coords)): + leds[led] = [leds[led][0] * persistance, leds[led][1] * persistance, leds[led][2] * persistance] + + # writing the new color data + for led in range(len(tree.coords)): + tree.set_led_RGB(led, [int(leds[led][0]), int(leds[led][1]), int(leds[led][2])]) + + tree.display() From 585f6042fc0f8b00a7ac176461ecc93cef947bd0 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Fri, 25 Dec 2020 12:42:04 +0100 Subject: [PATCH 06/10] added wavesoflight function --- treeshow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/treeshow.py b/treeshow.py index 18958fe..f465b57 100644 --- a/treeshow.py +++ b/treeshow.py @@ -1,7 +1,8 @@ from xmastrees import VirtualXmasTree, XmasTree + # user patterns -from spirolight import spirolight from xmaslights import xmaslights +from spirolight import spirolight from wavesoflight import wavesoflight def run_lights(): From 15fb53067ed8ca314556c4076d1475183b29b273 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Fri, 25 Dec 2020 15:05:21 +0100 Subject: [PATCH 07/10] Create README.md added general description of contents --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b8dd2a --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# xmastree2020 +My 500 LED xmas tree + +treeshow.py - runs the program +xmastrees.py - contains XmasTree (for running the hardware version) and VirtualXmasTree (for on-screen emulation) + +user patterns taking a tree as an argument: +xmaslights - original function (two colors spinning around a moving point) +spirolight - draws three color waves (RGB) going around the tree at different speeds +wavesoflight - draws waves of light starting at random points + +Xmastree objects are initiated with a path to the file containing led coordinates and they let you set the led color using the set_led_RGB(led_id, RGBcolor) method and update the whole tree using the display() method. From 5b28ffd1188bc187be674fd486b5febf97b6d079 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Fri, 25 Dec 2020 15:07:21 +0100 Subject: [PATCH 08/10] Update README.md formatting --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b8dd2a..2677528 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,14 @@ My 500 LED xmas tree treeshow.py - runs the program + xmastrees.py - contains XmasTree (for running the hardware version) and VirtualXmasTree (for on-screen emulation) + user patterns taking a tree as an argument: -xmaslights - original function (two colors spinning around a moving point) -spirolight - draws three color waves (RGB) going around the tree at different speeds -wavesoflight - draws waves of light starting at random points + +* xmaslights - original function (two colors spinning around a moving point) +* spirolight - draws three color waves (RGB) going around the tree at different speeds +* wavesoflight - draws waves of light starting at random points Xmastree objects are initiated with a path to the file containing led coordinates and they let you set the led color using the set_led_RGB(led_id, RGBcolor) method and update the whole tree using the display() method. From d53a6143b22c48b457e53582530a56f31f22404f Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Sat, 26 Dec 2020 11:56:25 +0100 Subject: [PATCH 09/10] added wavesoflight function --- wavesoflight.py | 64 ++++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/wavesoflight.py b/wavesoflight.py index dceb05a..c51731b 100644 --- a/wavesoflight.py +++ b/wavesoflight.py @@ -2,40 +2,56 @@ from random import randint +class Wave: + def __init__(self, tree): + self.radius = 0 + self.max_radius = 1000 + self.max_intensity = 100 + self.speed = 6 + self.source = randint(0, len(tree.coords)) + self.color = [randint(0, self.max_intensity), randint(0, self.max_intensity), randint(0, self.max_intensity)] + + def update(self, leds, tree): + self.radius += self.speed + # wave drawing + for led in range(len(leds)): + distance_from_source = math.dist(tree.coords[led], tree.coords[self.source]) + if self.radius - self.speed < distance_from_source <= self.radius: + for i in range(3): + if leds[led][i] < self.color[i]: + leds[led][i] = self.color[i] + + # function by pikuch def wavesoflight(tree): """Draws waves of light starting at random points""" - # current wave radius - radius = 0 - # maximum wave radius, after which it resets - max_radius = 800 - # the speed at which the wave spreads - wave_speed = 7 - # maximum intensity of any one colour channel - max_intensity = 100 - # wave source (led index) - source = randint(0, len(tree.coords)) + # list of waves + waves = [Wave(tree)] + # color persistance (1 = lasts forever) + persistance = 0.99 + # time between waves + wave_frequency = 60 + timer = wave_frequency # led colors leds = [[0, 0, 0]] * len(tree.coords) - # wave color - color = [randint(0, max_intensity), randint(0, max_intensity), randint(0, max_intensity)] - # color persistance (1 = stays forever, 0 = instantly off - persistance = 0.98 while True: - if radius > max_radius: + timer -= 1 + if timer <= 0: + timer = wave_frequency # create another wave - radius = 0 - source = randint(0, len(tree.coords)) - color = [randint(0, max_intensity), randint(0, max_intensity), randint(0, max_intensity)] + waves.append(Wave(tree)) - radius += wave_speed + for wave in waves: + wave.update(leds, tree) - # wave drawing - for led in range(len(tree.coords)): - distance_from_source = math.dist(tree.coords[led], tree.coords[source]) - if radius - wave_speed <= distance_from_source <= radius: - leds[led] = color + # remove old waves + to_remove = [] + for wave in waves: + if wave.radius > wave.max_radius: + to_remove.append(wave) + for wave in to_remove: + waves.remove(wave) # color dimming for led in range(len(tree.coords)): From dd6188e1b48953ceb7184412b60ca973172683d1 Mon Sep 17 00:00:00 2001 From: pikuch <55052790+pikuch@users.noreply.github.com> Date: Sat, 26 Dec 2020 11:57:35 +0100 Subject: [PATCH 10/10] improved wavesoflight - supports multiple waves at once --- treeshow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/treeshow.py b/treeshow.py index f465b57..b04d249 100644 --- a/treeshow.py +++ b/treeshow.py @@ -8,6 +8,7 @@ def run_lights(): coord_filename = "Python/coords.txt" + # choose the tree to use # tree = XmasTree(coord_filename) tree = VirtualXmasTree(coord_filename)