Skip to content

3D Plasma

import math
from gridmas import *

# derived from https://github.com/standupmaths/xmastree2020/blob/main/examples/3dplasma.py

# Play with these values to change how coarse the plasma effect is.
# Smaller value == faster
MATWX = 7
MATWY = 8
MATWZ = 25

# Set this value to lower the RGB (1 = full range, 0.5 = Half range, etc...)
dimLight = 0.8


class boundingBox():
    def __init__(self):
        self.minX = math.inf
        self.maxX = -math.inf
        self.minY = math.inf
        self.maxY = -math.inf
        self.minZ = math.inf
        self.maxZ = -math.inf
        self.wX = 0
        self.wY = 0
        self.wZ = 0

    def update(self, x, y, z):
        if self.minX > x:
            self.minX = x
        if self.maxX < x:
            self.maxX = x

        if self.minY > y:
            self.minY = y
        if self.maxY < y:
            self.maxY = y

        if self.minZ > z:
            self.minZ = z
        if self.maxZ < z:
            self.maxZ = z

    def finalize(self):
        self.wX = self.maxX - self.minX
        self.wY = self.maxY - self.minY
        self.wZ = self.maxZ - self.minZ

    def normalize(self, x, y, z):
        lx = (x - self.minX) / self.wX
        ly = (y - self.minY) / self.wY
        lz = (z - self.minZ) / self.wZ
        return lx, ly, lz


class matrix():
    def __init__(self, lx, ly, lz, bb):
        self._list = []
        for _ in range(lx * ly * lz):
            self._list.append([0, 0, 0])

        self._strideX = 1
        self._strideY = self._strideX * lx
        self._strideZ = self._strideY * ly
        self._bb = bb
        self._wX = lx
        self._wY = ly
        self._wZ = lz

    def get(self, x, y, z):
        return self._list[x * self._strideX + y * self._strideY + z * self._strideZ]

    def set(self, x, y, z, val):
        self._list[x * self._strideX + y * self._strideY + z * self._strideZ] = val

    def getTree(self, x, y, z):
        localX, localY, localZ = self._bb.normalize(x, y, z)
        localX = int(localX * (self._wX - 1))
        localY = int(localY * (self._wY - 1))
        localZ = int(localZ * (self._wZ - 1))
        return self.get(localX, localY, localZ)


def dist(x, y, z, wx, wy, wz):
    return math.sqrt((x - wx) * (x - wx) + (y - wy) * (y - wy) + (z - wz) * (z - wz))

coords = coords()


treeBB = boundingBox()
for i in coords:
    treeBB.update(i[0], i[1], i[2])

treeBB.finalize()

workMat = matrix(MATWX, MATWY, MATWZ, treeBB)

t = 0


def draw():
    global t

    for LED, pixel in enumerate(pixels()):
        pixel.set_rgb(*map(lambda x: int(x), workMat.getTree(coords[LED][0], coords[LED][1], coords[LED][2])))


    # Update the matrix
    for x in range(0, MATWX):
        for y in range(0, MATWY):
            for z in range(0, MATWZ):
                d1 = dist(x + t, y, z, MATWX, MATWY, MATWZ)
                d2 = dist(x, y, z, MATWX / 2, MATWY / 2, MATWZ)
                d3 = dist(x, y + t / 7, z, MATWX * 0.75, MATWY / 2, MATWZ)
                d4 = dist(x, y, z, MATWX * 0.75, MATWY, MATWZ)

                value = math.sin(d1 / 8) + math.sin(d2 / 8.0) + math.sin(d3 / 7.0) + math.sin(d4 / 8.0)

                colour = int((4 + value)) * 32
                r = min(colour, 255) * dimLight
                g = min(colour * 2, 255) * dimLight
                b = min(255 - colour, 255) * dimLight

                workMat.set(x, y, z, (g, r, b))
    t = t + 1