Block (Pixel) Stacker 1D

I searched everywhere in the forums and didn’t find any codes that have pixels “stacking” on themselves. So, I hope I didn’t reinvent the wheel by creating this code. It’s also my first PB pattern as I’m new to your world. I am fluent in PHP (and QBasic :flushed:) and have some experience with JS. So it’s not all foreign to me.

I’ve been working on this for a while now and have worked out most of the kinks myself. After several failed attempts of trial and error… along with reading tons of codes on this form… I’ve finally decided to ask for help. :slight_smile:

Finally, here’s my problem.

Start the pattern and it appears to skip the first lap/cycle completely (Even after the pattern is complete and it starts over). On the second lap/cycle, it will light up and hold the last block, but it will always be red. This is because the array didn’t get update on the first lap, so the last pixel in the array is always set to 0. I believe this is because of two things.

  1. The pixel array is offset by 1 because the array starts at [0] and we start counting at 1.
  2. pixel[pixels - 1] = h is called in the ELSE portion of render, but not in the IF.

I’ve tried adjusting to the offset of the array by adding or subtracting 1 from different vars, but none of them fix the problem. Just just move the problem left or right.

I’ve also tried to add pixel[pixels - 1] = h in the IF section of render. This enables the last pixel in the array to be captured, but then throws an array out of bounds error. This is because of how I’m counting… Say I have 16 pixels total. The array will be pixel[15] but on the first lap, it’s looking for pixel[16]. I’ve tried to adjust for this many ways without success.

I must be missing something here and I’m probably doing this the hardest way possible. I’m sure there’s codes I don’t know about yet that would make this cleaner and shorter. Feel free to tear it apart and tell me how I could (or should) have done it.

//Block Stacker by Nurples

//#####  Settings  #####//

//All settings are 0 ~ 1 value
speed = .005  //set the speed the block falls (Larger number = faster fall speed)
colorspeed = .1  //set the color change speed (larger number = slower color change)
BlockBrightness = .5  //set brightness for moving block (larger number = brighter)
StackBrightness = .5  //set brightness for stack of blocks (larger number = brighter)

//#####  End Settings  #####//


//set our vars
block = 0 
counter = 0
pixels = pixelCount
pixel = array(pixelCount)  //create an array to hold the stacked block's color


export function beforeRender(delta) {
  block += delta * speed    //Build the block speed
  
  if (block >= pixels) {
    block = 0               //Reset block location to the start
    counter++               //Count how many times we've reached the end (laps)(hit the stack)
    pixels = pixels - 1     //reduce pixel count each round to create the stacking effect
    if(counter >= pixels){  //reset our lap counter
      counter = 0
    }
  }  
  if (pixels < 0){pixels = pixelCount}  // Reset pixels so our loop doesn't just go black
} 

export function render(index) {
h = time(colorspeed)  //Set the moving block color
s = 1  //Set the saturation
v = 0  //Set the brightness to 0 so unused blocks are off

if (index <= pixels){          //Decide if the falling block is above the stack
  if (index == floor(block)){  //track the falling block
    v = BlockBrightness        //Set the brightness of the moving block
    //pixel[pixels - 1] = h      //Set the color of the top stacked block 
  } 
}
else
{
  pixel[pixels] = h  //Set the color of the top stacked block
  h = pixel[index]     //Display the saved color or remaining blocks
  v = StackBrightness  //Set the brightness of the stacked blocks
}
  hsv(h, s, v)
} 
export var block
export var counter
export var pixels  
export var pixel 

I’ve added extra vars just to give users more flexibility. Once i get it working properly, I plan to make it so the block sizes can be changed to more than just one pixel. I also want to make it 2D so that the blocks can be wider than one pixel also. :slight_smile: Feel free to throw tips at me for these idea, but please don’t give me the answers. I’ll never learn that way. :rofl:

Thanks in advance!!!
Nurples

Nurples, thanks for taking the leap to post about this! While I’m not going to look at your specific code tonight, I wanted to invite @zranger1 to consider posting his stacker (which I haven’t seen other than via email) as an example or reference. I also want to help you with yours so you gain skills, but this is what I could offer quickly.

1 Like

There’s no rush… I’m just diving into PB for fun. :grin:

Hi!
Here’s the stacker pattern @jeff mentioned. This one stacks pixels from both edges into the center, but shouldn’t be tough to make go only one way if that’s what you need.

There are UI controls for speed, number of segments and drawing mode. In drawing mode 0 (slider all the way left), it simply stacks the user selected colors. In mode 1 (roughly center) the “traveling” pixels are a user selected color, and the stacked pixels display a rainbow pattern. In mode 2 (slider to the right), it stacks semi-random complementary colors.

This pattern uses a lot of Pixelblaze-y techniques, and I’ll gladly answer any questions anyone might have on how it works! (Edit: Was working fast when I wrote this pattern and just noticed the near complete lack of helpful comments. Ugh! I’ll upload a better documented version to the library later.)

Pixel Stacker Pattern
/*
It's... 1D Tetris! Sort of.  
Subdivide the strip into 1 to 6 segments of equal length, then fill each segment 
with color from the edges to the center.  There are several color modes described below,
and sliders animation speed and fill "block" size.

colorMode 0: User selected solid color
colorMode 1: Animated Rainbow
colorMode 2: Color Bands

MIT License
8/26/2021 ZRanger1
*/

var h1 = 0.3333, s1 = 1,v1 = 1;
var h2 = 0.9, s2 = 1, v2 = 1;

var divisions = 2;
var count;
var center;
var pos;
var counter;

var colorMode = 0;
var nColorModes = 3;
var colorModes = array(nColorModes);
colorModes[0] =  (f) => h1;
colorModes[1] =  (f) => h1+t1+f/center
colorModes[2] =  (f) => .618 * round(f / size);


export var size = 4;
var halfSize = size / 2;

var frameTime;
var msPerFrame = 10

export function hsvPickerColor1(h,s,v) {
  h1 = h; s1 = s; v1 = v;
}

export function hsvPickerColor2(h,s,v) {
  h2 = h; s2 = s; v2 = v;
}

export function sliderSpeed(v) {
  msPerFrame = 250 * (1-v);
}

export function sliderSize(v) {
  size = 1+floor(10*v);
  halfSize = size / 2;
}

export function sliderSegments(v) {
  var n = 1+floor(v*5);
  if (divisions != n) {
    divisions = n;
    initSegments();
  }
}

export function sliderColorMode(v) {
  colorMode = floor(v * (nColorModes-1))
}

function initSegments() {
  count = floor(pixelCount / divisions)
  center = floor(count / 2);  
  pos = 0;
  counter = 0;  
}

initSegments();

var t1;
export function beforeRender(delta) {
  frameTime += delta;
  t1 = time(0.03);
  
  if (frameTime >= msPerFrame) {
    pos++;
    if (pos > (center-counter)) {
      counter+=size
      if (counter >= center) counter = 0;
      pos = 0;
    }
    frameTime = 0
  }
}

export function render(index) {
  index = index % count;
  index = center-abs(index-center);
  
  if (index > (center - counter)) {
//    var c = (colorMode) ? t1 + (index/center) : 0;
    hsv(colorModes[colorMode](index),s1,v1);
  }
  else if (abs(index - pos) < halfSize) {
    hsv(h2,s2,v2);
  }
  else {
    rgb(0,0,0);
  }
}
1 Like

OK, I was able to look at your code, understand it, and fix it! So I have some clues for you.

First, trace through the code in your head under the following conditions. For simplicity, let’s pretend you have 100 pixels. We are at the moment where we’ve animated the first block almost all the way to the end. Therefore, your variable named pixels is still set to 100, and your moving scanner/cursor named block is equal to, say, 99.9. Trace through your render(index) for when index is the last one – its highest value of 99. My question to you is, when/where/how in the code should this first hue be stored in pixel[99]?

If you can figure that out, I was able to place an additional conditional (if(){}) to catch that particular run of render(index) and store that hue in pixel[index]. My hint for you is that the additional conditional I added is nested in one of your existing cases.

Bigger picture though, I think two things would make your problem easier to reason through from the start:

  1. More descriptive variable names; variables that mean one specific thing. Instead of pixel[] try hues[]. Instead of block, try blockHeadPosition or blockX. Instead of pixels, try stackTop.
  2. Compute the world, its state, and all state changes in beforeRender(); ONLY render that world in render(). Don’t change that world in render().

Good luck!

1 Like

@zranger1 thanks for the example. I quickly read over the code. It’s probably a much simpler way than I would have built it. I still might build a stacker my way. I have a few ideas for a 2D stacker that I don’t see in yours. I’ll load yours and play with it later when I get time.

@jeff Thanks for the help! I’m currently on my phone. I read over your reply/questions/suggestions and I think I understand most of it. I’ll walk through your directions later and see what I come up with.

I’m sure I previously played around with your answer already on my own, but must have placed the if(){} in the wrong place or had incorrect data inside.

–Nurples

1 Like

Okay, I’ve done this and I’ve certainly attempted to do this before. BUT… What I realized this morning while reading other posts is that I never took into account that index starts at 0 just as an array does. I think I knew this before, but I forgot when writing the pattern. In my head, I was always imagining that index would go from 1 to 100 when it actually would go from 0 to 99.

I also knew that the first pattern was missing the code for saving the hue for the final pixel on the first lap. It’s actually commented out in my code above in the previous post. I also knew that it required an IF to make it happen… but, I could never get it to work because I was comparing index as if it was 100 and not 99. So that always returned strange results and not what I was expecting. Very frustrating. :rofl:

Here’s what I’ve got now with the adjustments:

//Block Stacker by Nurples

//#####  Settings  #####//

//All settings are 0 ~ 1 value
speed = .005  //set the speed the block falls (Larger number = faster fall speed)
colorspeed = .1  //set the color change speed (larger number = slower color change)
BlockBrightness = .5  //set brightness for moving block (larger number = brighter)
StackBrightness = .5  //set brightness for stack of blocks (larger number = brighter)

//#####  End Settings  #####//


//set our vars
block = 0 
counter = 0
pixels = pixelCount - 1
pixel = array(pixelCount)  //create an array to hold the stacked block's color


export function beforeRender(delta) {
  block += delta * speed    //Build the block speed
  
  if (block >= pixels) {
    block = 0               //Reset block location to the start
    counter++               //Count how many times we've reached the end (laps)(hit the stack)
    pixels = pixels - 1     //reduce pixel count each round to create the stacking effect
    if(counter >= pixels){  //reset our lap counter
      counter = 0
    }
  }  
  if (pixels < 0){pixels = pixelCount -1}  // Reset pixels so our loop doesn't just go black
} 

export function render(index) {
h = time(colorspeed)  //Set the moving block color
s = 1  //Set the saturation
v = 0  //Set the brightness to 0 so unused blocks are off

if (index <= pixels){          //Decide if the falling block is above the stack
  if (index == floor(block)){  //track the falling block
    v = BlockBrightness        //Set the brightness of the moving block
  } 
  if (index == pixelCount -1)
  {
    pixel[index] = h 
  }
}
else
{
  pixel[pixels] = h  //Set the color of the top stacked block
  h = pixel[index]     //Display the saved color or remaining blocks
  v = StackBrightness  //Set the brightness of the stacked blocks
}
  hsv(h, s, v)
} 
export var block
export var counter
export var pixels  
export var pixel 

The patter works as expected now.

I normally do this and yes, it would make everything more readable and easier to understand. When I started, I was thinking small, short, and to the point code… (save space) I also didn’t put notes in until I was finished. That too was difficult to do after the fact. :roll_eyes:

I think I understand this, but at the same time I’m questioning if I understand it. Did I do something in the code that would be in the opposite area? IE: Code in render() that should be in beforeRender()

@nurples, this latest version is great! I see one thing you could do to move a little more work out of render(), where it’s evaluated for every pixel, into beforeRender(), where it’s only done once per frame.

It’s this – the returned value of time() for a particular interval is memoized – it doesn’t change over the course of a single frame. So you could safely move the hue calculation back into beforeRender(). No big deal at all in this context. You’ll probably see that feature used in a lot of patterns. Just a useful, but not obvious “trick” you can put to work when you need it.

FWIW, I’ve just posted the documented version of my stacker pattern to the library. It’s really pretty basic – I look forward to seeing what you do, especially in 2D!

@zranger1 Thanks! I don’t think it’s too bad for what little I know about PB. I just did it from a programming POV. I don’t understand all the math and geometry stuff like some of you guys on here. It’s pretty complicated.

I’ll work on the pattern over the next few weeks as I get time. Maybe it will be something great… Or maybe it will stay just like it is. :rofl:

Thanks @zranger1 and @jeff for the assistance. PB seems like a fun hobby and if I stick to it, I’m sure I’ll have plenty more questions. I’ve still got the sensor board and expander board to play with. I’ve got some fun ideas for those someday too.

– Nurples

2 Likes

Well, welcome to the community and I hope you enjoy playing with your Pixelblaze! I’ve been doing it for a bit over a year now, and found it really relaxing – a great way to just have a little fun with programming.

1 Like

It’s minor, but:

`pixel[pixels] = h`

Seems to express the event that, OK, this block is done moving! It’s on the stack side now. Let’s persist its hue. I could see that fitting in your if (block >= pixels) { in beforeRender().

So glad you got it going, and thanks for being clear about how you wanted to be helped (a hint).

I’m not playing with my toys right now to test :rofl:, but I don’t this this would work. The var H isn’t set until the render() function and the hue changes as time goes by. I placed pixel[pixels] = h in render() to capture the final color when the block stops. In my head right now, I don’t see how moving it to beforeRender() would catch the final color. But, I’m probably wrong. :upside_down_face:

No problem! I always hated when people just pointed me to the manual or hinted at the solution instead of just giving me the answer, but over the years I’ve found it to be much more helpful when you have other problems. It’s frustrating at the moment, but once you figure it out and all the pieces come together you learn so much more.

I hope you’ll excuse me golfing you a little on this. You don’t have to expand the following if you want to try it yourself :grin:

Stacker--
//Block Stacker by Nurples

//#####  Settings  #####//

//All settings are 0 ~ 1 value
speed = .2  //set the speed the block falls (Larger number = faster fall speed)
colorspeed = .1  //set the color change speed (larger number = slower color change)
BlockBrightness = .5  //set brightness for moving block (larger number = brighter)
StackBrightness = .5  //set brightness for stack of blocks (larger number = brighter)

//#####  End Settings  #####//


//set our vars
export var block = 0
export var pixels = pixelCount
export var pixel = array(pixelCount)  //create an array to hold the stacked block's color
export var h

export function beforeRender(delta) {
  block += delta * speed    //Build the block speed
  h = time(colorspeed)  //Set the moving block color
  
  if (block >= pixels) {  // A block landed
    block = 0               //Reset block location to the start
    pixel[pixels - 1] = h   //persist hue of the block that just landed
    pixels--                //reduce pixel count each round to create the stacking effect
    if (pixels <= 0) pixels = pixelCount  //reset the whole thing
  }
} 

export function render(index) {
  var v = 0  //Set the brightness to 0 so unused blocks are off

  if (index >= pixels) {          //If this is the the stack
    h = pixel[index]     //Display the saved color of stacked blocks
    v = StackBrightness  //Set the brightness of the stacked blocks
  } else if (index == floor(block)) {  //If this is the falling block
    v = BlockBrightness        //Set the brightness of the moving block
  }
  hsv(h, 1, v)
} 
1 Like

Well, it’s certainly cleaner… And I assume it functions properly (I’ll test it out in the morning).

I see you moved h = time(colorspeed)to beforeRender(). I didn’t know this was possible. :nerd_face: I’ll figure out all the possibilities in time.

1 Like

Something else… I tried pixels-- and got nothing from it. I thought it was strange that pixels++ worked but not --. Odd…

I don’t believe -- is a supported operation, @wizard would have to add that explicitly. Remember this isn’t real JS, just similar. Only those commands implemented actually work.

Also congrats on the pattern. When “the experts” golf (like @jeff above), consider it a compliment, and look at the golf as a way to see how others would tweak it, not that you did anything incorrect. There are lots of ways to do the same things. Some are slightly faster, or “neat tricks” but so long as the pattern works, that’s the real answer.

As for the fancy math and geometry, once the basic tutorial series I’m working on is completed, the “math and geometry” will be the next tutorial series. It’s not nearly as hard as you think, once you grasp the concepts. If you want to learn more, there are lots of videos out there that introduce these concepts and more, in clear simple ways (unrelated to LEDs of course, that’s the piece I’ll be adding in)

1 Like

@Scruffynerf I don’t mind that he fixed it up. :slight_smile: It looks more presentable and actually works a little smoother than my version. As you said, there’s lots of ways to come to the same result in PB. I like to see and learn from other people techniques.

-- must have been added recently then, because it seems to be working. But I’m almost positive I tried pixels-- and got no result. I may have tried in on 3.18~3.19 and maybe @wizard added this functionality recently?

@jeff Looking at the VARs output, I don’t think I ever would have come up with this method on my own. The VAR output h only updates on the first rotation which would have (and still does a little) confuse me.

On your version, the block lands first, then the next block starts. This is a smoother look than mine where the block lands and the next one starts at the same time.

1 Like

I think this might be a minor bug or glitch in the watcher (@wizard). I noticed this too, and I think if you rename h to anything else, it updates more than just the first time!

Also, I don’t know when – was added, but I thought I’ve been using it for a long time. ++ isn’t technically in the docs either.

So, for now… Thanks to @jeff… I think I’ve got a pretty neat 1D Stacking Pattern going. Today I modified it to so that the stack sizes can be modified also. Fell free to show me how I could have done that eaiser/cleaner. @jeff :wink:

Next, I think, is to make it 2D. :smirk: Not sure I’m ready for that yet.

Block Stacker 1D
//Block Stacker by Nurples
//Thanks Jeff for the assist! ;-)

//#####  Settings  #####//

//All settings are 0 ~ 1 value
speed = .05  //set the speed the block falls (Larger number = faster fall speed)
colorspeed = .1  //set the color change speed (larger number = slower color change)
BlockBrightness = .5  //set brightness for moving block (larger number = brighter)
StackBrightness = .5  //set brightness for stack of blocks (larger number = brighter)
BlockSize = 10  //set the block length in pixels 

//#####  End Settings  #####//


//set our vars
export var block = 0
export var pixels = pixelCount
export var pixel = array(pixelCount)  //create an array to hold the stacked block's color
export var h
BlockSizeCount = BlockSize 

export function beforeRender(delta) {
  block += delta * speed    //Build the block speed
  h = time(colorspeed)  //Set the moving block color
  
  if (block >= pixels) {  // A block landed
    block = 0               //Reset block location to the start
    while(BlockSizeCount > 0 && pixels > 0) //capture all landing blocks within the BlockSize
    { 
      pixel[pixels - 1] = h   //persist hue of the block(s) that just landed 
      pixels--  //reduce pixel count each round to create the stacking effect
      BlockSizeCount-- //reduce BlockSizeCount 
    }
    if(BlockSizeCount >= 0) BlockSizeCount = BlockSize
                   
    if (pixels <= 0) pixels = pixelCount  //reset the whole thing
  }
} 

export function render(index) {
  var v = 0  //Set the brightness to 0 so unused blocks are off

  if (index >= pixels) {          //If this is the the stack
    h = pixel[index]     //Display the saved color of stacked blocks
    v = StackBrightness  //Set the brightness of the stacked blocks
  } else if ((index+BlockSize) >= floor(block) && (index+BlockSize) < floor(block)+BlockSize) {  //If this is the falling block(s)
    v = BlockBrightness       //Set the brightness of the moving block
  } 
  hsv(h, 1, v)
}

How do you guys quote code in the forum so that it’s collapsible?
Edit: Thanks @zranger1

1 Like

To hide stuff!

At the far right of the message editor’s toolbar, there’s a gear icon. Click it to get to the “Hide Details” option. It’ll drop a block like this into your message:

[details="Summary"]
This text will be hidden
[/details]

Change the details=title, and drop whatever you want to collapse between [details] and [/details]

2 Likes