This is my attempt at a ‘Canada Day’ pattern, It gives alternating sections of red and white, with optional fading between red/white. The length of the sections, direction, speed, brightness can all be adjusted via sliders. My sliders have standard names as they are controlled by home automation (so you know why some of the names are weird).
The LED strip runs along the underside of my garden fence, and is 1440 pixels long.
I think the pattern could be optimized, and so I have some questions, which more experienced people may be able to help out with.
This is the output:
This is the full pattern:
// masking on by default
export var masked = 1
var mask = array(pixelCount)
// set all pixels ON
arrayMutate(mask, () => 1)
function setMasked(on) {
// set/clear masked pixels
mask[354]=mask[355]=mask[356]=mask[357]=mask[358]=mask[359]=mask[734]=mask[735]=mask[736]=mask[737]=mask[738]=mask[739]=mask[740]=mask[741] = !on
mask[888]=mask[889]=mask[890]=mask[891]=mask[892]=mask[893]=mask[894]=mask[895]=mask[960]=mask[961]=mask[962]=mask[963]=mask[964]=mask[965]=mask[1080] = !on
mask[1081]=mask[1082]=mask[1083]=mask[1084]=mask[1085]=mask[1086]=mask[1087]=mask[1088]=mask[1222]=mask[1223]=mask[1224]=mask[1225]=mask[1226] = !on
mask[1227]=mask[1228]=mask[1229]=mask[1230]=mask[1231]=mask[1232]=mask[1233]=mask[1355]=mask[1356]=mask[1357]=mask[1358]=mask[1359]=mask[1360] = !on
mask[1361]=mask[1362]=mask[1363]=mask[1364]=mask[1365]=mask[1366]=mask[1367] = !on
}
setMasked(masked)
export function sliderMask(x) {
masked = (x > 0.5)
setMasked(masked)
}
var PATHLIGHT_LED = 1223 //first LED that covers the path ( this is the fence post - up to 12 before this is the actual path)
export var max_brightness = 1
export var fps = 1
export var hue_increment = 1
export var min_brightness = 0
export var saturation = 1
export var pathlight = 0
export var hue = 0
export var timing = 0 //not used
export var direction = 1
export var num_sections = pixelCount/100 //num_sections is the pixelCount/length of red+white sections
export var fade = 1/(pixelCount/num_sections/6) //6 is the fraction of pixels to fade 6=1/3 of the pixels of a specific colour
//(ie 1/(2*3) of each colour), so 36 white pixels would fade up/down in 12 pixels.
export function sliderStep(_v) {
// in this pattern step (hue_increment) is used to select the nuber of sections
hue_increment = clamp(_v, 0.01, 1) //don't allow 0
num_sections = pixelCount/(hue_increment*200) //pixelCount/200 = min number sections
fade =1/(pixelCount/num_sections/6)
}
export function sliderSpeed(_v) {
fps = .01 + (3.99 * _v)
// When slider is full left, _v == 0, speed is .01.
// When slider is full right, _v == 1, speed is 4.
}
export function sliderMaxBrightness(_v) {
max_brightness = _v
}
export function sliderMinBrightness(_v) {
// in this pattern minBrightness is used to select direction and fading, not minBrightness
min_brightness = _v
//direction
direction = (_v < 0.5) ? -1 : 1
}
export function sliderPathlight(_v) {
pathlight = _v
}
export function beforeRender(delta) {
t1 = time(0.65 / 65.536 / fps) // t1 repeats every 0.65 seconds
}
export function render(index) {
pos = index/pixelCount //counts from 0-1 with index
v = (1 + pos * num_sections + t1*direction) % 1 //v ramping up/down from 0-1 to create sections
//fade up/down
if (min_brightness < 0.3 || min_brightness >= 0.7) {
saturation = clamp(saturation + fade*((v>0.5) ? 1: -1), 0, 1)
//saturation = (v>0.5) ? clamp(saturation + fade, 0,1) : clamp(saturation - fade, 0,1) // same as above, but easier to read
}
//sharp transition 0.5 is 50% white, 50% red
else saturation = (v<0.5)
br = max_brightness
if (pathlight > 0 && index >= PATHLIGHT_LED) {
saturation = 0
br = pathlight
}
br *= mask[index]
// 0 is red
hsv(0, saturation, br*br*br)
}
Masking:
Certain pixels are blanked out (fence post verticals for instance). I can’t find a good way of turning certain pixels off without using a huge array.
// masking on by default
export var masked = 1
var mask = array(pixelCount)
// set all pixels ON
arrayMutate(mask, () => 1)
function setMasked(on) {
// set/clear masked pixels
mask[354]=mask[355]=mask[356]=mask[357]=mask[358]=mask[359]=mask[734]=mask[735]=mask[736]=mask[737]=mask[738]=mask[739]=mask[740]=mask[741] = !on
mask[888]=mask[889]=mask[890]=mask[891]=mask[892]=mask[893]=mask[894]=mask[895]=mask[960]=mask[961]=mask[962]=mask[963]=mask[964]=mask[965]=mask[1080] = !on
mask[1081]=mask[1082]=mask[1083]=mask[1084]=mask[1085]=mask[1086]=mask[1087]=mask[1088]=mask[1222]=mask[1223]=mask[1224]=mask[1225]=mask[1226] = !on
mask[1227]=mask[1228]=mask[1229]=mask[1230]=mask[1231]=mask[1232]=mask[1233]=mask[1355]=mask[1356]=mask[1357]=mask[1358]=mask[1359]=mask[1360] = !on
mask[1361]=mask[1362]=mask[1363]=mask[1364]=mask[1365]=mask[1366]=mask[1367] = !on
}
setMasked(masked)
export function sliderMask(x) {
masked = (x > 0.5)
setMasked(masked)
}
The brightness is multiplied by mask[index]
before calling hsv
, which sets the index
pixel to black (off) if the array value is 0. sliderMask()
allows turning masking on and off. This uses an array of pixelCount
size, which is mostly not used. I can’t find a way of defining a const array of values though.
Is there a better way of doing this? is there no way to make a table?
Alternating sections:
v = (1 + pos * num_sections + t1*direction) % 1 //v ramping up/down from 0-1 to create sections
v ramps up and down between 0-1 to create the sections. This looks clunky, and I feel that there has to be a clever method using square()
to alternate the segments - but I can’t figure it out. It has to go in render()
. There must be an easier way of doing this!
Fading up/down:
saturation = clamp(saturation + fade*((v>0.5) ? 1: -1), 0, 1)
Saturation fades between 0-1 to fade from red/white/red and I’m using a fixed fade value, determined by the length of the section.
Fading between two colours or 0-1 is such a basic thing, there must be a good way of doing it. This line seems to involve a lot of maths for no reason (ie I’m adding/removing fade then clamping it).
Pathlight:
I have a path running down one side of the garden, when pathlight
is set to anything over 0, that LED’s along the path go white at pathlight
brightness. This is how I do it:
if (pathlight > 0 && index >= PATHLIGHT_LED) {
saturation = 0
br = pathlight
}
Is this the best way of doing this? I don’t seem to be able to return multiple values (like an object) from a function - maybe defining an hsv array might be better and using that.
Would I be better creating a pixel buffer array, and rendering the whole thing in before_render()
, and just displaying the array in render()
.
I just want an idea of how best to do these things before I charge off in the wrong direction with a bunch of patterns. A lot of this code is boilerplate, and is used in all my patterns.
I get 20 fps at 1MHz frequency with this pattern, which is perfectly fine. I just have a feeling that I’m overcomplicating things.
Suggestions welcome!