Advice on 'Canada Day' pattern

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:
canada_day

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!

@jeff has an awesome video for this (link to original forum post).

(I’ll add more later, I just wanted to add this first, as it’s a good overview of this sort of approach)

You can return an array, see my Complex Number code for an example.

I’m going to say it depends on if you need access to the index… if this is a fixed layout, where you know specific index values should be left alone (or whatever), then you might be better off… but really if you can calculate it all that way, what’s the advantage to using before_render? Might as well make the code simpler. Sometimes pixel buffers make sense, but usually you need to have a need for data (what pixels are lit, for example) for them to do so.

In this case, with a set of pixels that you’ve mapped (like fender post verticals), I think you’re doing it right: the mask lets you override pixels by location. You need render’s awareness of index, though, to decide on the location, and compare to your mask

Doing the opposite in the name of a smaller array, (so a list of just what pixels to mask), would be a smaller array, but you’d have to do more work to pull out the data. Is this pixel one of the masked ones, you’d have to look thru the entire array, because there is no easy ‘array contains’ type function. Might as well make the lookup trivial, faster at the expense of size. The ‘noneasy’ version uses arrayReduce.

  • Count instances of value: key = valueyouseek; arrayReduce(a, (acc, v) => acc + (v == valueyouseek), 0)

Thanks!

This is my new render:

export function render(index) {
  pos = index/pixelCount  //counts from 0-1 with index
  v = square(pos*num_sections - t1*direction, .5) // .5 => 50% red

  //fade up/down
  if (min_brightness < 0.3 || min_brightness >= 0.7) {
    saturation = clamp(saturation + fade*((v) ? 1: -1), 0, 1)
  }
  //sharp transition 50% white, 50% red
  else saturation = v

  br = max_brightness*max_brightness*max_brightness
  br *= mask[index]

  if (pathlight > 0 && index >= PATHLIGHT_LED) {
    saturation = 0
    br = pathlight*pathlight*pathlight
  }
  
  // 0 is red
  hsv(0, saturation, br) 
}

So, using square() works.

I considered using arrayReduce, but then I would have to search the whole array for each pixel, and while the array is not large, it seemed to be a lot of searching.

So the only remaining question is the fade section:
Is this the best way to do it?

//fade up/down
  if (min_brightness < 0.3 || min_brightness >= 0.7) {
    saturation = clamp(saturation + fade*((v) ? 1: -1), 0, 1)
  }

Given that v is now 0 or 1 (white/red).

Thanks for the input.

Update, I did add a test, so that fade is only applied when neccessary:

  //fade up/down
  if ((min_brightness < 0.3 || min_brightness >= 0.7) && v!=saturation) {
    saturation = clamp(saturation + fade*((v) ? 1: -1), 0, 1)
  }

Just makes me feel better.

1 Like