module: colors

colors is a module which contains class definitions for Color and Pixel as well as helper functions for converting colors between formats

I appologise to all british programmers who spell color as colour, but within the programming world we spell it color. This will be the cause of 90% of your bugs if you're not use to programming with the color spelling

class: Color

A data class representing a color

from colors import Color
@staticmethod
Color(r: int, g: int, b: int) -> Color

initiate a color instance, values between 0 and 255 for R, G, and B

red = Color(255, 0, 0)
@staticmethod
white() -> Color

The color white

white = Color.white()
@staticmethod
black() -> Color

The color black (turn the LED off)

black = Color.black()
@staticmethod
from_hex(s: str) -> Color

get a color from a string hex code

red = Color.from_hex("FF0000")
@staticmethod
from_hsl(hue: float, sat: float, lig: float) -> Color

get a color from hsl format

red = Color.from_hsl(0, 1, 0.5)
@staticmethod
random(saturation: float = 1, lightness: float = 0.6) -> Color

Generate a random color (generate random hue for hsl), saturation and lightness can be specified

my_random_color = Color.random() my_dark_random_color = Color.random(lightness=0.2)
@staticmethod
different_from(color: Color) -> Color

Generate a random color which is different from the passed color maintaining the same hue and saturation

red = Color.red() not_red = Color.different_from(red)
set_rgb(r: int, g: int, b: int)

set the red green blue values of the color, all bound between 0 and 255

light = tree.get_light(200) light.set_rgb(255, 0, 0) # the light is now red
set_color(color: Color)

change the color to another color

light = tree.get_light(200) light.set_color(Color.red()) # the light is now red
fade(n: float = 1.1)

fade the color slightly, greater values of n will fade quicker, whereas values less than 0 will brighten to a max of white

light = tree.get_light(200) light.fade() # the light is now slightly darker
to_tuple() -> tuple[int, int, int]

Returns a tuple of the R, G, and B values between 0 and 255

light = tree.get_light(200) light.set_color(Color.red()) # the light is now red
to_hex() -> string

Returns the hex value of the RGB color

red = Color.red() print(red.to_hex()) # prints out "FF0000"
lerp(target: tuple[int, int, int], frames: int, override: boolean = False, fn: Callable[[float], float] = util.linear)

Linearly interpolate the color from its current color to the target color (a tuple of red green and blue 0 - 255) over the amount of frames. This was initially based on the lerp found in the Unity Game Engine. set override to true to force a reset of the interpolation, it will take the current value and start interpolation from there

Each successive call to lerp will step the color toward the target color. After frame amount of calls, it will be the target color. any change to the target or the frames between calls will restart the lerp from the current color, and interpolate to the new target over frames

you can pass in any interpolation function which maps a float (0 - 1) to another float (0 - 1). Util provides lots based on the css timing functions

1 2 3 4 5 6 7 8 my_color = Color.red() # (255, 0, 0) my_color.lerp((0, 0, 0), 5) # (205, 0, 0) my_color.lerp((0, 0, 0), 5) # (153, 0, 0) my_color.lerp((0, 0, 0), 5) # (102, 0, 0) my_color.lerp((0, 0, 0), 5) # (51, 0, 0) my_color.lerp((0, 0, 0), 5) # (0, 0, 0) # once reacing the target, lerp has no effect my_color.lerp((0, 0, 0), 5) # (0, 0, 0)

typically apply lerp to each pixel once a frame in the main loop

1 2 3 4 5 6 7 8 9 10 11 def main(): # a little tracer program, i = 0 while True: # turn on each light in a row each frame i = (i + 1) % tree.num_pixels tree.get_light(i).set_color(Color.white()) # tell each pixel to go to interpolate to black for pixel in tree.pixels: pixel.lerp((0, 0, 0), 30) tree.update()

and optionally you can provide a timing function to override the linear default

1 2 3 4 5 6 7 8 9 10 11 12 import util def main(): # a little tracer program, i = 0 while True: # turn on each light in a row each frame i = (i + 1) % tree.num_pixels tree.get_light(i).set_color(Color.white()) # tell each pixel to go to interpolate to black for pixel in tree.pixels: pixel.lerp((0, 0, 0), 30, fn=util.ease_in_out_sine) tree.update()
set_lerp(target: tuple[int, int, int], time: int, override: boolean = False, fn: Callable[[float], float] = util.linear)

this will reset the lerp and start interpolation to target from current value. sucessive calls will not change the target unless override is set to True. use with cont_lerp to have the same effect as lerp()

cont_lerp()

this will advance the linear interpolation one step

class: Pixel extends Color

the pixel class extends the Color class by adding 3D coordinates to a color. All the same methods and attributes exist on a pixel so they act the same way

coordintates are in the GIFT format so range between -1 and 1 on X and Y axis, and 0 and tree.height on the Z axis

Methods:
Attributes:xyzad
x: float

the X coordinate of the pixel

1 2 3 4 5 6 # set one half of the tree to red the other to green for pixel in tree.pixels: if pixel.x > 0: pixel.set_rgb(255, 0, 0) else: pixel.set_rgb(0, 255, 0)
y: float

the Y coordinate of the pixel

1 2 3 4 5 6 # set one half of the tree to red the other to green for pixel in tree.pixels: if pixel.y > 0: pixel.set_rgb(255, 0, 0) else: pixel.set_rgb(0, 255, 0)
z: float

the Z coordinate of the pixel

1 2 3 4 5 6 # set one half of the tree to red the other to green for pixel in tree.pixels: if pixel.z > tree.height / 2: pixel.set_rgb(255, 0, 0) else: pixel.set_rgb(0, 255, 0)

polar coordinates

polar coordinates can be used to locate a pixel with the height pixel.z, angle from x+ pixel.a, and distance from z axis pixel.d its 3D position is defined

a: float

the polar angle in radians from the x axis going clockwise when looking downward on the tree

1 2 3 4 5 6 # set one half of the tree to red the other to green for pixel in tree.pixels: if pixel.a > math.pi: pixel.set_rgb(255, 0, 0) else: pixel.set_rgb(0, 255, 0)
d: float

the polar distance from the Z axis (trunk)

1 2 3 4 5 6 # set an inner cylinder to red and outer to green, it is assumed the base radius is 1 for pixel in tree.pixels: if pixel.d > 0.5: pixel.set_rgb(255, 0, 0) else: pixel.set_rgb(0, 255, 0)

Utility Functions

just some helper function which might be useful

# import them like this from colors import tuple2hex
int2tuple(c: int) -> tuple[int, int, int]

conver the 24bit encoded int to tuple of R, G, and B.

int bitmap encoded as GGGGGGGGRRRRRRRRBBBBBBBB

tuple2hex(t: tuple[int, int, int]) -> str

conver an RGB tuple to hex string

1 print(tuple2hex((0, 0, 0))) # prints "000000"
hex2tuple(h: str) -> tuple[int, int, int]

conver a hex string to an RGB tuple

1 print(hex2tuple("ffffff")) # prints "(255, 255, 255)"