Trying to modify KITT(and failing)

Hi all,

I’m trying to modify the KITT patterns in 2 ways: I would like it to be one-directional, and also keep the background pixels lit. The second part is ok, I asked how to do it long ago and got all the help I needed, but now I’m stuck in how to make it one-directional. If I remove the part that reverses direction, when the leader resets all the less of the strip flash at full intensity. I know it’s the last bit of the code that’s responsible for this, but I can’t really decipher it so any help would be much appreciated:)

leader = 0
direction = 1
pixels = array(pixelCount)

export function sliderHue(v) {
  Hue = (v);
}

export function sliderSpeed(v) {
speed = (v / 12 +.01);
}


//export var fade
export function sliderFade(v) {
fade = ((1-v) /300 +0.00016 );
}



export function beforeRender(delta) {
  lastLeader = floor(leader)
  leader +=  delta * speed
  
  if (leader >= pixelCount) {
   
    leader = 0 
  }
  
  
  up = lastLeader < leader 
  for (i = lastLeader; i != floor(leader); up ? i++ : i-- ) pixels[i] = 1
    
  for (i = 0; i < pixelCount; i++) {
    pixels[i] -= delta * fade
    pixels[i] = max(0.4, pixels[i])
  }
}

export function render(index) {
  v = pixels[index]
  v = v * v * v
   h = (v > 0.002) ? 0 : 1  
  hsv(Hue, 1, (h == 0) ? v : 0.5)  
}

This is the key part of code:

You’ve got the code to jump the leader to the start! The next bit of code draws pixels between the lastLeader and new leader position. In normal KITT, this is so that if a leader jumps more than 1 pixel location it doesn’t leave gaps. But thats exactly what you want (gaps) if leader is resetting to zero.

So, if we only want to run that code when we aren’t resetting leader, we can move it inside an else block, right off the bit before it.

  if (leader >= pixelCount) {
    leader = 0 
  } else {
    //when not resetting leader to zero, draw to fill any gaps between lastLeader and leader
    up = lastLeader < leader 
    for (i = lastLeader; i != floor(leader); up ? i++ : i-- ) pixels[i] = 1
  }
    

In the new code, the up variable is never going to be false (in a meaningful way), since we don’t reverse direction. We have some refactoring to do, but thats easy enough to remove and the code is simpler afterward:

  if (leader >= pixelCount) {
    leader = 0 
  } else {
    //when not resetting leader to zero, draw to fill any gaps between lastLeader and leader
    for (i = lastLeader; i != floor(leader); i++) pixels[i] = 1
  }

For background pixels, I assume you mean that the “tail” should fade some, but not all the way to black?

Take a look at this part of the code, which handles fades:

  for (i = 0; i < pixelCount; i++) {
    pixels[i] -= delta * fade
    pixels[i] = max(0, pixels[i])
  }

It will subtract some value from all pixels, and then prevent it from staying negative with max(0, pixels[i]). The max function returns the largest of 2 values. You can “raise the floor” by using a non-zero value here, and fades won’t go darker than that value.

1 Like

Thanks a lot for such a thorough reply!

By doing the modifications, the last pixel of the string doesn’t get updated, I solved it by adding a + 1 in this line:

 if (leader >= (pixelCount + 1 )) {
    leader = 0 

What’s the correct way to add a delay between each cycle? I tried playing with time and delta between the two lines above where I guess is the place to do it, but got only funky or no results at all.

Correct way in PB to “do a delay”:

In beforerender, add Delta to and check a timer variable to see if it’s still time to “do nothing” or if time is up, clear/zero the variable. Then in render, check that variable for zero, if so, do stuff, otherwise do nothing, so it’ll “wait” for the timer. (It won’t wait, it’ll just run before render again, that’s the 2 stroke engine of PB: before render, render, before render, render… Over and over. If you ever try to “stall it” and “pause” you’re doing it wrong, use the above instead)

In your case, set the timer to 1 when you hit the end of a line. Then the before render function will count Delta (number of millis since last time it ran), and you’ll have to decide how long to “pause” and then set the timer to zero when it’s high enough (aka long enough)

Alternatively you could countdown, but the logic is a bit more convoluted, as you’d only run if the timer is equal or less than zero, only subtract Delta if it’s above zero, and set the timer to whatever # of millis you want to pause when you are at the end. I think counting up is clearer than counting down.

That’s pseudocode. Hopefully it’s clear enough.

@Scruffynerf

Thanks for spending the time to write the pseudocode, even though I’m stuck I prefer it to being spoon fed a solution otherwise I’ll never learn:)

I added a timer in beforerender:

timer = time(0.03)

When I introduce an if statement either within beforerender or render, the timer variable gets stuck to that value instead of counting. I can’t figure out why, shouldn’t “if” just check what the variable is at that time instead of changing it?

Only if I use < or > instead of = the timer will keep running, but then the animation will either freeze(if it’s done within beforerender) or the strip will turn off(if done within render) until the counter resets.

I think you dont understand how time() works. Search the forum, I know we have discussions about it. It’s not what most people think. It’s a loop from 0 to 1, and restarts at 0, and the duration of that loop gets longer or shorter, based on the value, with .15 being about 1 second long.

I’ll write up an actual code example when I find some time. As a volunteer forum, we try and help people but it’s all based on trying to educate others.

Also, the syntax of checking for equals is ==
If you do this:

if (a = 1) { something something}

You aren’t checking if a is 1, you are setting a to 1

@Scruffynerf Sorry for taking so long to reply and thanks once again for the help.

I realized my mistake was placing the if statement in the render function. By doing that the animation would stop at set intervals regardless of whether it had completed its cycle or not. By placing it at the end of the loop within beforeRender, I’m getting what I need, once the animated pixels are > end of the index, it will pause, then resume after whatever I’ve put in time().

Something I often come upon but never figured out and need once again in this pattern, if I want to add a slider for a function but the min/max I need are not easily divided(eg. I cannot simply do (v / 10)), but something like 0.2 to 0.8, how would I do it? Is there a function that does it, or is it only math?

btw, I’m looking forward to your upcoming tutorial for beginners:)

1 Like

You’re going to laugh when you see how easy it is to add a Min and a Max to a slider:

export function sliderSample(slidevalue) {
   min = .2
   max = .8
   finalvalue = min + (max-min)*slidevalue
}

You could also use clamp() but that’s ugly and makes dead zones on either side of the slider.
That
min + (max-min)*(some value between 0..1)
is a simple mix or lerp function but we don’t have it yet, though we’ve asked @wizard for it. It can sometimes be found as
start * (1 - t) + end * t
which, if you do the math, is the same but isn’t as clear as to how it works as the above, and also requires one more math operation (4 vs 3), so my version is potentially slightly faster.

I use that kind of scaling often enough that I made a function for it:

function scaleBetween(value, min, max) {
  return (min + value * (max - min));
}

and use it like this:

var scaledValue = 0.2;
export function sliderSample(sliderValue) { 
    scaledValue = scaleBetween(sliderValue, 0.2, 0.8); 
}
var randomValue = scaleBetween(random(1), 20, 36);
var randomAngle = scaleBetween(random(1), 0, PI2);
1 Like

Yeah, it needs to be a built in function, it’s an essential tool.

1 Like

Yeah, lerp was on my list for sure, I can see scale(value, start, end) being handy too.

Scale as above, lerp, and mix, are all the same function. Arguments are usually ordered as A,B,T where T is the 0…1 value, A is min/start, B is max/end.

1 Like

Very simple, and indeed far better than clamp. Thanks!

1 Like

Also, this variant is a log scale slider which allows you to have a slider which stretches in both directions for when you want both small values and large values. Say .01 to 10. If you just use 0-10 (slider value x 10), very small numbers are hard to pick. Using log, you can create a slider where the midpoint is something like 1, and the values on one side go toward small values, and on the other, go toward big ones.

var minv = log(.1);
var maxv = log(10);

export function sliderScale(v){
  scalefactor = exp(minv + (maxv-minv)*v);
}

As you can see, it’s the same principle, just using log() values and exp() to make the final value fall into the right ranges. You could also do 10 to 100, or 10 to 1000 this way, better than just making it a linear slider of min/max.