Pattern making for the cube

118 views
Skip to first unread message

J C

unread,
Sep 21, 2017, 3:18:28 AM9/21/17
to leeds-ha...@googlegroups.com
Alex is hosting a workshop this sunday from 14:30 to get people making more patterns for lightnight. 

The patterns are created using python and you will need some experience with python or a similar language to engage in the workshop. 

More details to come from Alex soon. Let us know on this thread if you plan to come along. 

You dont need to attend the workshop to make a pattern. Some sample patterns and a demo helper can be found here https://github.com/pbrook/pycubedemo

If you want to submit a pattern for light night please get it to us for 11pm 30th september if music needs adding to it. 8pm wednesday the 4th October if it alrrady has its own music/sounds. 

If you want to get involved in making music to go with new patterns then join up with Samson. 


George Scott

unread,
Sep 21, 2017, 8:23:43 AM9/21/17
to leeds-ha...@googlegroups.com
I'm in

George 

--
You received this message because you are subscribed to the Google Groups "Leeds Hack Space" group.
To unsubscribe from this group and stop receiving emails from it, send an email to leeds-hack-space+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

samson B

unread,
Sep 21, 2017, 6:01:17 PM9/21/17
to leeds-ha...@googlegroups.com
ill be there :)

Alex Silcock

unread,
Sep 23, 2017, 4:47:54 PM9/23/17
to leeds-ha...@googlegroups.com
So that we can get going more quickly tomorrow, could everyone who's planning to attend please set up as much of the following as possible before the workshop tomorrow.

Instructions will vary based on whether you're using Windows, Linux or OS X, but they should largely follow these steps.

0. Bring your own laptop
Tom, Paul and I have been trying to set up a couple of Hackspace laptops with everything set up, but it's been proving difficult, so please bring your own.

1. Get Python
The workshop will be in Python so please ensure you have it installed. Both Python 2.7 and Python 3 are fine, although it seems as though you'll have fewer problems with Python 2.7 (due to issues with pygame).

2. Get the pycubedemo code
We'll be writing patterns using an existing framework of Python code for interacting with the cube, which is located at https://github.com/leedshackspace/pycubedemo (note that this repository is a fork of the one Joe mentioned in the original email).
If you're familiar with git then it'll be easiest for you to clone the repo, but if not, you can click "Clone or Download" then click "Download ZIP" to download it as a zip file. If you do this though, it'll be more work to fetch any extra updates that I make to the code tomorrow.
If you'd like to try out git and haven't before, this introduction looks alright: https://rogerdudler.github.io/git-guide/

3. Install the pycubedemo dependencies
The Python code depends on a couple of Python packages, which are described in the README on the GitHub page linked above. If you try for ages and can't get pygame installed, have a look at the web-based simulator linked at the bottom of the GitHub page.

4. Try running the demo patterns
If you've got everything downloaded and installed correctly, you should be able to run this command from your terminal and see some patterns displayed in a pop-up window:

python cube.py

It should look something like this:

Inline images 1 

If you get to this point, then you're all set for tomorrow! If you have any issues then post in this email thread and hopefully we'll be able to help you get setup in advance, but if not, then people will be available to help with any issues at the workshop.

See you tomorrow!

Parsley

unread,
Sep 23, 2017, 5:01:13 PM9/23/17
to Leeds Hack Space
PS: It is most important to come with ideas. All the ideas! In glorious 8x8x8 resolution!

Just be prepared for a lot of trial and error in finding good patterns, as some will work better than others :]

Alex Silcock

unread,
Sep 23, 2017, 6:53:03 PM9/23/17
to leeds-ha...@googlegroups.com
Something else you can use - if you want to make an interactive pattern, we have 4 Android tablets which will be connected to a private wireless network, so you can make a pattern that members of the public can interact with through a web browser on one of the tablets.

On 23 Sep 2017 10:01 pm, "Parsley" <tooshor...@gmail.com> wrote:
PS: It is most important to come with ideas. All the ideas! In glorious 8x8x8 resolution!

Just be prepared for a lot of trial and error in finding good patterns, as some will work better than others :]

Ian Kernick

unread,
Sep 25, 2017, 6:38:07 PM9/25/17
to Leeds Hack Space
I've written a pattern that seems to work fine in the simulator.

Currently, it's using a 3-dimensional array containing a boolean flag for every pixel. I haven't seen any of the existing examples contain such a large array, so I was wondering if there are limitations to what the LED controller can handle?

Here's my code if I wasn't clear enough, it's still a WIP.

# Rainbow Explosions
# Copyright (C) Ian Kernick

import cubehelper
import random

DT = 1.0/64

class Pattern(object):
def init(self):
self.cube.size
self.neighbours_coords = []
self.neighbours_colors = []
return DT
def getRandColour(self, color):
seed = random.randint(0,5)
hi = 0.2
md = 0.0
lo = 0.0
if seed == 0:
r = hi
g = md
b = lo
if seed == 1:
r = hi
b = md
g = lo
if seed == 2:
b = hi
r = md
g = lo
if seed == 3:
b = hi
g = md
r = lo
if seed == 4:
g = hi
r = md
b = lo
if seed == 5:
g = hi
b = md
r = lo
if r > 1.0:
r = 1.0
if g > 1.0:
g = 1.0
if b > 1.0:
b = 1.0
return (color[0]-r, color[1]-g, color[2]-b)
def addNeighbours(self, coord, color):
(x, y, z) = coord
if x != 0 and not self.filled[x-1][y][z]:
self.neighbours_coords.append((x-1,y,z))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x-1][y][z] = True
if  x != self.cube.size-1 and not self.filled[x+1][y][z]:
self.neighbours_coords.append((x+1,y,z))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x+1][y][z] = True
if y != 0 and not self.filled[x][y-1][z]:
self.neighbours_coords.append((x,y-1,z))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x][y-1][z] = True
if  y != self.cube.size-1 and not self.filled[x][y+1][z]:
self.neighbours_coords.append((x,y+1,z))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x][y+1][z] = True
if z != 0 and not self.filled[x][y][z-1]:
self.neighbours_coords.append((x,y,z-1))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x][y][z-1] = True
if  z != self.cube.size-1 and not self.filled[x][y][z+1]:
self.neighbours_coords.append((x,y,z+1))
self.neighbours_colors.append(self.getRandColour(color))
self.filled[x][y][z+1] = True
def drawPixel(self, coord, color):
self.cube.set_pixel(coord, color)
(x, y, z) = coord
self.addNeighbours(coord, color)
def newStart(self):
self.cube.clear()
self.filled = [[[False for x in range(self.cube.size)] for y in range(self.cube.size)] for z in range(self.cube.size)]
x = random.randint(0,self.cube.size-1);
y = random.randint(0,self.cube.size-1);
z = random.randint(0,self.cube.size-1);
self.drawPixel((x, y, z), (1.0, 1.0, 1.0))
def tick(self):
for i in range(0, 3):
if len(self.neighbours_coords) == 0:
self.newStart()
print 'reset'
else:
id = random.randint(0, len(self.neighbours_coords)-1)
self.drawPixel(self.neighbours_coords[id], self.neighbours_colors[id])
self.neighbours_coords.pop(id)
self.neighbours_colors.pop(id)
if False:
raise StopIteration




Ian Kernick

unread,
Sep 28, 2017, 8:25:00 AM9/28/17
to Leeds Hack Space
Hey folks,

What are we to do with our code submissions?

Is there a separate chat somewhere to discuss the organising of this?

samson B

unread,
Sep 30, 2017, 10:53:50 AM9/30/17
to leeds-ha...@googlegroups.com
hi all im putting together sound banks for people to use here is a link to what iv done so far for the snake game feel free to ask me if you want any sounds making and everything in this folder is free to use for hack space madness :)


--

Alex Silcock

unread,
Sep 30, 2017, 11:14:20 AM9/30/17
to leeds-ha...@googlegroups.com
Hi Ian, 

Thanks for making a pattern! The patterns themselves will be running on a laptop connected to the cube for Light Night so I wouldn't imagine you'd have any performance problems, and your code looks fine to me, but I'll have a look at it on my laptop later just to be sure.
I can add your pattern to the repository for you if you like, or if you use GitHub you can submit it yourself by forking the repo at github.com/leedshackspace/pycubedemo, pushing your code in a new branch on your fork, and then raising a pull request to our pycubedemo repo. There's some more info on how to do it here if you'd like to try: https://gist.github.com/Chaser324/ce0505fbed06b947d962.

Thanks,
Alex



Sent from a mobile device


--

Alex Silcock

unread,
Sep 30, 2017, 11:24:20 PM9/30/17
to leeds-ha...@googlegroups.com
Looks good Ian. I've raised a pull request on GitHub here for it: https://github.com/leedshackspace/pycubedemo/pull/8

You mentioned in your email that it was still a WIP, so I won't merge the PR until you've got back in touch.

Ian Kernick

unread,
Oct 3, 2017, 6:35:29 AM10/3/17
to Leeds Hack Space
Thanks for the info Alex.

I've made some changes to my code since my last message. I'll be in the HS this evening to test it out (if the cube is available).

I was planning to make some sounds for it too. Is there a particular library that we're using for playing audio?

Thanks

Alex Silcock

unread,
Oct 3, 2017, 6:50:40 PM10/3/17
to leeds-ha...@googlegroups.com
Hi Ian,

Did you get chance to test out your pattern on the cube? I arrived to the space fairly late this evening so I think I missed you.

We've been using pygame's mixer to play sound effects in existing patterns, here are some examples of how to set it up and use it.

I'm working on putting the sequence together for Light Night at the moment, so would you be able to send your finished pattern and sounds through as soon as possible?

Cheers
Alex

--

Ian Kernick

unread,
Oct 4, 2017, 10:48:41 AM10/4/17
to Leeds Hack Space
Hi Alex,

Sorry didn't manage to see you yesterday. Due to an emergency, I had to skip visiting the Hackspace. I did manage to finish the animation part of my code last night, though.

I've also put in some hooks for playing audio. I'm going to pop into the Hackspace on my way back from work. I'll quickly whip up some sounds to test it with and then send it over to you.
To unsubscribe from this group and stop receiving emails from it, send an email to leeds-hack-spa...@googlegroups.com.

Ian Kernick

unread,
Oct 4, 2017, 2:38:34 PM10/4/17
to Leeds Hack Space
I tried using github and I can't update my initial branch now.

Here's the latest version anyway. My sound isn't playing properly, but I'm working on it.

# Rainbow Fill
# Copyright (C) Ian Kernick

import cubehelper
import random
import numpy
import pygame

DT = 1.0/64
MOVE_ANIM_LENGTH = 128 # must be a multiple of 8
FADE_ANIM_LENGTH = 128

# Directions:
# x+ = 0
# x- = 5
# y+ = 1
# y- = 4
# z+ = 2
# z- = 3

class Pattern(object):
def init(self):
self.anim_counter = 0
self.MOVE = 0
self.FILL = 1
self.FADE = 2
self.state = self.MOVE
self.double_buffer = True
pygame.init()
pygame.mixer.pre_init()
pygame.mixer.init()
pygame.mixer.set_num_channels(24)
self.fill_sound = pygame.mixer.Sound('patterns/rainbowfill-data/fill.wav')
self.move_sound = pygame.mixer.Sound('patterns/rainbowfill-data/move.wav')
self.neighbours_coords = []
self.neighbours_colors = []
self.direction = random.randint(0,5)
self.origin_x = random.randint(0,self.cube.size-1)
self.origin_y = random.randint(0,self.cube.size-1)
self.origin_z = random.randint(0,self.cube.size-1)
self.filled = [[[False for x in range(self.cube.size)] for y in range(self.cube.size)] for z in range(self.cube.size)]
self.pixels = [[[(0.0,0.0,0.0) for x in range(self.cube.size)] for y in range(self.cube.size)] for z in range(self.cube.size)]
return DT
def validateDirection(self):
# Make sure the origin position doesn't move out of the cube's boundaries
if ((self.new_direction == 0 and self.origin_x == self.cube.size -1) or
(self.new_direction == 5 and self.origin_x == 0) or
(self.new_direction == 1 and self.origin_y == self.cube.size -1) or
(self.new_direction == 4 and self.origin_y == 0) or
(self.new_direction == 2 and self.origin_z == self.cube.size -1) or
(self.new_direction == 3 and self.origin_z == 0)):
return False
else:
return True
def updateOriginPosition(self):
# Move the origin position in a direction
self.cube.set_pixel((self.origin_x, self.origin_y, self.origin_z), (0.0, 0.0, 0.0))
if (self.direction == 0):
self.origin_x += 1
elif (self.direction == 5):
self.origin_x -= 1
elif (self.direction == 1):
self.origin_y += 1
elif (self.direction == 4):
self.origin_y -= 1
elif (self.direction == 2):
self.origin_z += 1
elif (self.direction == 3):
self.origin_z -= 1
self.cube.set_pixel((self.origin_x, self.origin_y, self.origin_z), (1.0, 1.0, 1.0))
def drawPixel(self, coord, color):
# Renders the chosen pixel to the cube and then calls findNeighbours()
self.cube.set_pixel(coord, color)
self.pixels[coord[0]][coord[1]][coord[2]] = color
self.findNeighbours(coord, color)
def findNeighbours(self, coord, color):
# Searches for adjacent pixels that have not been previously added to neighbours_coords
if color != (0.0,0.0,0.0):
(x, y, z) = coord
if x != 0 and not self.filled[x-1][y][z]:
self.addNeighbour((x-1,y,z),color)
if  x != self.cube.size-1 and not self.filled[x+1][y][z]:
self.addNeighbour((x+1,y,z),color)
if y != 0 and not self.filled[x][y-1][z]:
self.addNeighbour((x,y-1,z),color)
if  y != self.cube.size-1 and not self.filled[x][y+1][z]:
self.addNeighbour((x,y+1,z),color)
if z != 0 and not self.filled[x][y][z-1]:
self.addNeighbour((x,y,z-1),color)
if  z != self.cube.size-1 and not self.filled[x][y][z+1]:
self.addNeighbour((x,y,z+1),color)
def addNeighbour(self, coord, color):
# Adds the neighbouring pixel to neighbours_coords and assigns it a colour (but doesn't render it yet)
self.neighbours_coords.append(coord)
self.neighbours_colors.append(self.getRandColour(color))
self.filled[coord[0]][coord[1]][coord[2]] = True
def getRandColour(self, color):
# Randomly reduces one of the rgb colour channels by a value
colorList = list(color)
i = random.randint(0,2)
colorList[i] -= 0.25
if colorList[i] < 0:
colorList[i] = 0.0
return (colorList[0], colorList[1], colorList[2])
def tick(self):
if self.state == self.MOVE:
# Move the origin pixel 8 times during the animation
if self.anim_counter%(MOVE_ANIM_LENGTH/8.0) < 1.0:
# Change the direction the origin pixel 4 times during the end animation
if self.anim_counter%(MOVE_ANIM_LENGTH/4.0) < 1.0:
# The new direction can't be the opposite of the old direction
self.new_direction = (5 + random.randint(1,5) - self.direction)%6
# If the origin pixel hits an edge, change the direction
self.direction_valid = self.validateDirection();
while not self.direction_valid:
self.new_direction = (5 + random.randint(1,5) - self.direction)%6
self.direction_valid = self.validateDirection();
# Update the origin pixels position and play sound
self.direction = self.new_direction
self.updateOriginPosition()
self.move_sound.play()
self.anim_counter += 1
if self.anim_counter == MOVE_ANIM_LENGTH - 1:
self.state = self.FILL
self.filled = [[[False for x in range(self.cube.size)] for y in range(self.cube.size)] for z in range(self.cube.size)]
self.filled[self.origin_x][self.origin_y][self.origin_z] = True
self.anim_counter = 0
elif self.state == self.FILL:
if self.neighbours_coords == []:
# First frame
if self.anim_counter == 0:
self.drawPixel((self.origin_x, self.origin_y, self.origin_z), (1.0, 1.0, 1.0))
self.fill_sound.play()
self.anim_counter += 1
# Last frame
else:
self.state = self.FADE
self.anim_counter = 0
# Middle frames
else:
id = random.randint(0, int(len(self.neighbours_coords)*0.8))
self.drawPixel(self.neighbours_coords[id], self.neighbours_colors[id])
self.neighbours_coords.pop(id)
self.neighbours_colors.pop(id)
elif self.state == self.FADE:
decay = (FADE_ANIM_LENGTH-self.anim_counter-1)/float(FADE_ANIM_LENGTH)
for x in range(self.cube.size):
for y in range(self.cube.size):
for z in range(self.cube.size):
self.cube.set_pixel((x,y,z), numpy.multiply(self.pixels[x][y][z],decay))
self.cube.set_pixel((self.origin_x, self.origin_y, self.origin_z), (1.0, 1.0, 1.0))
self.anim_counter += 1
if self.anim_counter == FADE_ANIM_LENGTH - 1:
self.state = self.MOVE
self.anim_counter = 0

Ian Kernick

unread,
Oct 4, 2017, 6:27:50 PM10/4/17
to Leeds Hack Space
I finally got the sound working.

I've pushed everything to here, I hope you have access to it:

Sorry for the messy branches, I promise I'll learn how to use Github properly soon.

Thanks

Alex Silcock

unread,
Oct 5, 2017, 7:43:34 AM10/5/17
to leeds-ha...@googlegroups.com
Looks good to me Ian, I'll try to get it integrated into the show when I finish it off tonight. Thanks!

--

samson B

unread,
Oct 5, 2017, 8:15:30 PM10/5/17
to leeds-ha...@googlegroups.com
alex how about this its attributes free from you tube library 
Monitors.mp3
Reply all
Reply to author
Forward
0 new messages