Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sierpinski triangle #25

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
plt.style.use("dark_background")

coords = list(map(eval, open("coords.txt").readlines()))
x = list(c[0] for c in coords)
y = list(c[1] for c in coords)
z = list(c[2] for c in coords)

class board:
D18 = None

class neopixel:
class NeoPixel:
def __init__(self, _pin, n, *args, **kwargs):
self.pixels = [(0, 0, 0)] * n
self.ax = plt.axes(projection="3d")

def __setitem__(self, index, color):
self.pixels[index] = (color[1] / 255.0, color[0] / 255.0, color[2] / 255.0, 1)

def show(self):
self.ax.scatter(x, y, z, c=self.pixels)
plt.pause(1 / 1000)
self.ax.cla()
221 changes: 139 additions & 82 deletions xmaslights-spin.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,137 @@

def area(x1, y1, x2, y2, x3, y3):
""" A utility function to calculate area of triangle formed by (x1, y1), (x2, y2) and (x3, y3)

Downloaded from: https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
"""
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)



def isInside(x1, y1, x2, y2, x3, y3, x, y):
"""
A function to check whether point P(x, y) lies inside the triangle formed by A(x1, y1), B(x2, y2) and C(x3, y3)

Function downloaded from: https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
"""
# Calculate area of triangle ABC
A = area (x1, y1, x2, y2, x3, y3)

# Calculate area of triangle PBC
A1 = area (x, y, x2, y2, x3, y3)

# Calculate area of triangle PAC
A2 = area (x1, y1, x, y, x3, y3)

# Calculate area of triangle PAB
A3 = area (x1, y1, x2, y2, x, y)

# Check if sum of A1, A2 and A3
# is same as A
if(A == A1 + A2 + A3):
return True
else:
return False


def point_inside_triangle(triangle, point):
"""
Checks if a 3D point lies inside a 3D triangle shape
"""
p1, p2, p3, p4 = triangle

p1x, p1y, p1z = p1
p2x, p2y, p2z = p2
p3x, p3y, p3z = p3
p4x, p4y, p4z = p4

px, py, pz = point

# I don't know if this is mathematically correct for all cases of triangle, but it works well enough for my simple triangle
# in a non-safety critical cristmas tree
xz =isInside(p1x, p1z, p2x, p2z, p4x, p4z, px, pz) # projection on the x-z plane
xy =isInside(p1x, p1y, p2x, p2y, p3x, p3y, px, py) # projection on the xy plane
yz = isInside(p3y, p3z, p1y, p1z, p4y, p4z, py, pz) # projection on the yz plane
return xz and xy and yz


def mid(p1, p2):
""" Returns the midpoint between 2 3D points """
return (p1[0]+p2[0])/2, (p1[1]+p2[1])/2, (p1[2]+p2[2])/2

def draw_triangle(coords, triangle, colour):
""" A debug function I initially used for a 2D triangle approach. can be removed now """
# Draw the matplotlib stuff
x = [line[0] for line in triangle]
y = [line[1] for line in triangle]
plt.fill(x, y, c=[a/255.0 for a in colour])


def draw_triangle_xmastree(coords, pixels, triangle, colour):
for LED, coordinate in enumerate(coords):
x, y, z = coordinate
if point_inside_triangle(triangle, (x, y,z)):
pixels[LED] = colour
# TODO perhaps we want to not show it here sometimes, or do actually show it here for longer than is currently happening
pixels.show()


def sierpinsky(coords, pixels, triangle, depth, maxdepth):
# Only draw the sierpinsky triangle up to a maximum depth
if depth > maxdepth:
return

# Draw the current triangle on the christmas tree
draw_triangle_xmastree(coords, pixels, triangle, colours[depth])

# Each triangle consists of four ordered points (x,y,z).
# In my case I like them counterclockwise starting with the bottom-left point, bottom first
p1, p2, p3, p4 = triangle

# Find the new bottom midpoints of this 3D triangle
mid1 = mid(p1, p2)
mid2 = mid(p2, p3)
mid3 = mid(p3,p1)

# Find the new top midpoints of this 3D triangle
mid4 = mid(p1, p4)
mid5 = mid(p2, p4)
mid6 = mid(p3, p4)

# Recursively update the triangle with the next depth
sierpinsky(coords, pixels, [p1, mid1, mid3, mid4], depth+1, maxdepth)
sierpinsky(coords, pixels, [mid1, p2, mid2, mid5], depth+1, maxdepth)
sierpinsky(coords, pixels, [mid3, mid2, p3, mid6], depth+1, maxdepth)
sierpinsky(coords, pixels, [mid4, mid5, mid6, p4], depth+1, maxdepth)



# Global colours to be used in the christmas tree function
colours = [[250,250,250], [250,0,0], [0,0,250], [0,250,0]]


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

# use real leds
import board
import neopixel

# use simulator
# from sim import board
# from sim import neopixel


# You are welcome to add any of these:
# import random
# import numpy
# import numpy as np
# import scipy
# import sys

Expand All @@ -22,7 +141,7 @@ def xmaslight():

# IMPORT THE COORDINATES (please don't break this bit)

coordfilename = "Python/coords.txt"
coordfilename = "coords.txt"

fin = open(coordfilename,'r')
coords_raw = fin.readlines()
Expand All @@ -37,6 +156,7 @@ def xmaslight():
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

Expand All @@ -54,91 +174,28 @@ def xmaslight():
max_alt = max(heights)

# VARIOUS SETTINGS

# how much the rotation points moves each time
dinc = 1

# a buffer so it does not hit to extreme top or bottom of the tree
buffer = 200

# pause between cycles (normally zero as it is already quite slow)
slow = 0

# startin 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


# INITIALISE SOME VALUES

swap01 = 0
swap02 = 0

# direct it move in
direction = -1

# the starting point on the vertical axis
c = 100

# yes, I just run which run is true
run = 1
while run == 1:

# The initial triangle. You can change the coordinates here a bit if you want the triangle bigger or smaller
triangle = [
[-300, -300, -400],
[300, -300, -400],
[0, 300, -400],
[0, 0, 400],
]

time.sleep(slow)

LED = 0
while LED < len(coords):
if math.tan(angle)*coords[LED][1] <= coords[LED][2]+c:
pixels[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()

# now we get ready for the next cycle

angle += inc
if angle > 2*math.pi:
angle -= 2*math.pi
swap01 = 0
swap02 = 0

# this is all to keep track of which colour is 'on top'

if angle >= 0.5*math.pi:
if swap01 == 0:
colour_hold = [i for i in colourA]
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]
colourB = [i for i in colour_hold]
swap02 = 1

# and we move the rotation point
c += direction*dinc
# You can recurse all you want, but a max depth of 3 is already slow enough...
# At some point the christmas tree is simply not dense enough to appreciate more recursion.
for maxdepth in range(3):
sierpinsky(coords, pixels, triangle, 0, maxdepth)

# To be honest, I don't know if I should let the christmas tree sleep at some point or not. The initial phases (not too much recursion) go a bit too fast, while the final phase (very deep recursion) is a bit too slow IMO.
# time.sleep(1)

if c <= min_alt+buffer:
direction = 1
if c >= max_alt-buffer:
direction = -1

return 'DONE'


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