Task #6: Enter the Matrix

Welcome to Task #6.
This will require a matrix panel, or equivalent, of at least 8x8 (larger is fine)

We’re going to start easy:

Render2D requires a map, that puts all pixels into x and y coordinates.

So you can effectively consider all pixels will be between 0 and 1, on both X and Y axes.

Let’s start with picking a random point, picking a random color, and then setting that point to that color.

Repeat.

We’ll build on this next week. As I said, this is a simple start.

This week’s madness, as requested, picks a random set of x,y coordinates and draws a dot at that location. The
twist is that you can control the size of the dot with a slider. When I started writing this, I noticed that my larger dots were rarely symmetrical. A little thinking revealed that this was because the random numbers I was generating were rarely or never landing on pixel centers.

Solving this problem requires knowing the dimensions of the matrix. At this writing, there’s no straightforward way for a pattern to get that information, which is why you occasionally see matrix dimensions hard coded even in patterns that support render2D(). Here’s a way, only slightly hack-o-riffic, to get matrix dimensions on the fly:

// variables to help find matrix boundaries;
var sizeFound = 0;        
var xMax,yMax; 
var renderer = renderMapDimensions;

// variables used to generate random dot
var frameTimer = 9999;
var x1,y1,hue;

// variables used by UI
export var holdTime = 250;
export var dotSize = 1;

// UI Sliders
export function sliderDotSize(v) {
  dotSize = 0.5+10 * (v * v);
}

export function sliderHoldTime(v) {
  holdTime = 50 + (1500 * v);
}

// This is a sneaky way of determining the map dimensions for
// rectangular matrices while rendering (while not rendering, actually)
// the first frame. Will not work well on irregular 2D shapes.
// Far as I know, there's no direct way to get map data from a
// pattern right now.
// We do this because unless we know where the pixel centers are,
// it's difficult to generate random numbers that land on pixel centers.
function renderMapDimensions(index,x,y) {
  if (!sizeFound && x >= 0.999) {
    xMax = index;
    yMax = -1+ (pixelCount / (xMax+1));
    sizeFound = 1;
  }
}

// Render our random dot. Yay Pythagoras!!!  
function renderRandomDot(index,x,y) {
  x = ((x * xMax) - x1) ; y = ((y * yMax) - y1);
  bri = sqrt((x * x) + (y * y)) < dotSize;
  hsv(hue, 1, bri)  
}

// uses a straightforward timing mechanism based on 
// accumulating delta to periodically generate new
// random coordinates and color. 
export function beforeRender(delta) {
  frameTimer += delta
  if (sizeFound) renderer = renderRandomDot;
  
  if (frameTimer > holdTime) {
    frameTimer = 0;
    x1 = floor(random(xMax+1));
    y1 = floor(random(yMax+1));
    hue = random(1);
  }
}

export function render2D(index,x,y) {
  renderer(index,x,y);
}
3 Likes

Great code! I marked-up most of this for self/noobs. Here are a few select notes and related comments/questions -

  1. Sliders - e.g.

Left number is lower bound, right plus left is upper bound, use v-squared, v-cubed and etc. for increased “sensitivity”.

This bit is indeed fun and sneaky (once figured out) seems it would work in 3D also. “!” is a logical “not”, returns reverse logical value. So, if sizefound is > 0.999 AND x is >= 0.999 then you have found the range in X = width = index. And so on.

Not so sure about this , tho -

Seems the front-end “-1” is redundant?
sizefound = 1 kicks it out of any more iterating on xMax.

Nice to know one can just make up render functions and work them like -

OK. That’s it for this AM. On to figuring the spots… !

1 Like

Here’s what’s going on with that line:
(pixelCount / xMax+1) calculates the number of rows. In the case of a 16x16 matrix, this would be… 16. What I actually want for later ease of use though, is the index of the last row, which is 15. Therefore, the -1+(pixelCount/xMax+1).

1 Like

Ok, I got it with wanting the index not the width. Lost track of which was in focus in the instance.

But, anyway, I’ve fiddled with this Task today for long enough. I tried it a good while back and gave up. Nothing I do seems to work. And I still do not get mapping or how the mapper tab integrates with the code I write…

Here’s my recent go at it. All it does is flash the whole matrix . I tried “scaling” calls of x and y to 0 to 1 to no avail. Something trivial no doubt, but, help please?

export var DwellTime
var timer
var width = 16
var height = pixelCount / width
var x1, y1

export function sliderDwellTime(v) {
DwellTime = 50 + (2500 * v)}

export function beforeRender(delta) {
timer += delta

if (timer >= DwellTime) {
timer = 0
x1 = floor(random(width))
y1 = floor(random(height))
hue = clamp(random(1),0.1,0.9)

}
}

export function render2D(index,x,y) {
if (x = x1)
if (y = y1) {

hsv(hue,1,1) 

}

else {

hsv(0,0,0) }

}

Thanks!

I’ll take a look tomorrow when I spend some time on the next Task (which I’m still mulling)

@JustPete, you’ve found one of the classics: In the two if statements in your render function, you’re using the assignment operator =, when I think you mean to use the comparison operator ```==````.

export function render2D(index,x,y) {
if (x = x1)     // should be 'if (x == x1) 
if (y = y1) {  // 'if (y == y1)

I admit to having done this more than once myself. It’s a problem in most popular computer languages – easy to do, and hard to find. “Bigger” development tools might warn you about it, and some modern languages make the operators easier to tell apart, but it’s really just one of those things you have to watch out for.

In beforeRender, you’re setting x1,y1 to be integers from 0 to width and 0 to height. In render() though, x and y come in normalized to the range 0 to 1 according to your mapping function. To get back to integer coordinates that can be easily compared, you need to multiply them by width and height:

if (floor(x * width) == x1)
if (floor(y * height) == y1) {

also, the arrangement of if statements – if (condition1) if (condition2) { do things; } – looks like it ought to work but it doesn’t. I have no idea if it’s supposed to.

Anyway, there’s a surprising amount of wiggle room in language specification. Best practice is to be very explicit about what you want done, and put in all the braces. (Or make this a compound conditional using the logical AND operator &&. Like this: if ((condition1) && (condition2)) { do things;} )

Here’s your render function with these modifications made. Your design was correct – it works perfectly.

export function render2D(index,x,y) {
  if (floor(x * width)  == x1) {
    if (floor(y * height) == y1) {
      hsv(hue,1,1) 
    }
  }
  else {
    hsv(0,0,0)
  }
}
2 Likes

Indeed, this could be viewed as some kind of code epistemology, 'the nature of “equals”, but I see what you mean. Code may be flexibly configured but intolerant of interpretation, clearly demonstrated in this instance. Thanks.

Normalizing this was, I thought something I has tried, but I get lost in what I have and have not tried when it does not work for a while… It is useful to me to understand the code in before render can be whatever, in render it has to be normalized to the 0 to 1 range.

Interesting note (as yet unexplored), my 16 x 16 matrix displays two adjacent pixels in y when displaying the last column, but not always.

Thanks much for filling me in @zranger1

No, that’s incorrect.

PB as part of mapping anything, changes the map into a range from 0…1
So if you give 1, 2, 3, 4, 5(for example), when you actually get the map, it’s 0, .25, .5, .75, and 1

This is different from Index, value which is always 0 thru the number of pixels (minus 1)

The mapping change is meant to be helpful, so that no matter what values you put in, you always get a consistent range back out, so any pattern can just “work”, rather than insist on having some specific value. For multiple examples of the opposite of this, look at any of matrix-y patterns that require setting the width and height, and break if you don’t. Done with a map, it doesn’t matter if you have 8x8, 16x16 or 32x64, the map is still 0,0 in one corner and 1,1 in the opposite corner.

Render has nothing to do with this. Remember it just gives you Index (actual pixel number) and if 2D also gives you X (0…1), and Y (also 0…1).

What you do with that is up to you.

Yup. That would be a misstatement on my part. This index plus coordinates thing is just alright with me.

5 posts were split to a new topic: How does Pixelblaze coordinates mapping work?