How does Pixelblaze coordinates mapping work?

Hello all you LED Wizards out there. I really don’t get it! If my matrix is 12x22 and the first pixel is 0,0 and the last pixel is 1,1 how does Pixelblaze know that my matrix is 12x22? How does this magic work?

1 Like

It doesn’t know that. It only knows that you gave it 264 points, all of which fall between 0 and 1 on each axis. (Even if you give it other values, it’ll convert them all to 0…1 range) It has no idea if you have 12 or 14 or 17 points in a row… In fact, you could… It doesn’t require square points, or even division of pixels (which is the HUGE advantage of PB mapping, over the 2D mapping of most led systems which can ONLY do square/rectangular maps)

Hi Scruffynerf, yes I do get that, I just don’t know how to config the code that states that. So, for example, is this all I need to code into the Matrix page?

if (floor(x * width)  == x1) {
    if (floor(y * height) == y1) {
      hsv(hue,1,1)

Sounds almost too simple to me.
Thanks

See the other thread. By Matrix page, I think you mean the mapping page. Which is NOT where you’d do hsv() anything. It’s purely a real JavaScript (unlike PB) program that either generates an array of coordinates OR is literally an array of coordinates.

If you mean in an actual pattern, yes, that could might work, but it’s what I consider “brittle” requiring someone to add height and width variables that are hard coded. So you put in 12 and 22 and it works for you, but then you share the pattern and someone with a cylinder that is 15x30, it doesn’t work for them, until they adjust the height and width.

Ideally your code shouldn’t require those, and work regardless of the actual dimensions, and thus work on any map, any size, any number of pixels, etc.

But we have “brittle” patterns in the collection now, and I’ve even written some of them, but the goal is to avoid that going forward. The transformation API (which has scaling and rotating) really helps with this too.

1 Like

To follow up on this: @zranger’s original answer above was written pre-Transformation API, but these days, with it, I suspect the code is much cleaner and easier to write ‘agnostic’ (without height/width), and do the right thing. if @zranger doesn’t beat me to it, I’ll rewrite it to the ‘less brittle’ method, as an example of a solution for this task.

Hi @FredEBear ,

Most Pixelblaze patterns are written to work on coordinates scaled to “world units” which is a unit that covers the entire mapped area.

Your code:

This looks like pattern code that accepts coordinates in world units, and could convert them back to pixels with a known width and height, and snap them to integer values. It looks like it might be used to conditionally paint a pixel at a (x1, y1) coordinate, and nothing else. You could totally do it that way, by defining your width and height in the pattern.

If you move (x1, y1) around, as integer values the pixel will jump over to the next, it won’t smoothly animate. Another way you could express that pattern is by setting the brightness to the closeness of a given pixel to that coordinate. In this way a coordinate animating around could smoothly transition between pixels and appear anti-aliased.

Here’s a roughly similar pattern rewritten to use world coordinates, with smooth sub pixel animation. The the x1 and y2 coordinates are set to animate (tracing a Lissajous curve):

var pixelDensity = 12
var x1, y1
var hue = 0
export function beforeRender(delta) {
  x1 = wave(time(.1))
  y1 = wave(time(.075))
}

export function render2D(index, x, y) {
  var distance = hypot(x1 - x, y1 - y)
  var closeness = clamp(1 - distance * pixelDensity, 0, 1)
  hsv(hue,1,closeness)
}

The distance is a small number, given that we’re working in world units. But we want closeness! We can scale distance up, and then invert it so that it is larger the closer it is (towards 1), and falls off rapidly the further away it gets. The clamp() keeps closeness to a value between 0 and 1, otherwise it would go negative as the distance increases.

The pixelDensity scales the dot down from world-sized to some smaller fraction. A value 1.5X the actual pixel density creates sharp pixel sized dots. It doesn’t have to exactly match though, and would still work on any 2D pixel mapped setup, even irregular maps, animating the dot around the entire “world.”

2 Likes

I feel like such a dork as I really don’t understand “world units” and how to describe them within the Matrix setup section. I tried pasting this into that page and it shows an 8x8 matrix.

var pixelDensity = 12
var x1, y1
var hue = 0
export function beforeRender(delta) {
  x1 = wave(time(.1))
  y1 = wave(time(.075))
}

export function render2D(index, x, y) {
  var distance = hypot(x1 - x, y1 - y)
  var closeness = clamp(1 - distance * pixelDensity, 0, 1)
  hsv(hue,1,closeness)
}

I think the part that’s hard to grasp is that other then the Pixel density at 12, I see no other areas within the code that allows me to tell Pixelblaze the configueration of my matrix. I get it that 0,0 is the first LED and 1,1 is the last led but I can’t wrap my head around placing the necessary data within those paramaters to establish a viable matrix. What’s the secret for the layman? How can anyone understand this stuff. Where is the Pixelblaze for Dummies info?

@FredEBear Try this page (is this the same as the GitHub page you mention?)

Mapping in Pixelblaze

I’m interested in the specific questions it leaves you with (mainly because I wrote it and I wanted it to be just above “For Dummies” level).


If you’re wondering why the code doesn’t need to know the number of pixels, think about your computer display. Why can people with a 1900x1200 display as well as people with a 2560x1440 display both view the same PDF? Imagine this PDF just had a gigantic lowercase letter “o”. It’s because fonts (and this PDF) don’t define pixels or bitmaps, they define shapes (AKA vectors). In essence, the code you pasted just defines a circle; it doesn’t specify which pixels should make up a circle. That’s why the same Pixelblaze code can work on an 8x8 as well as a 16x16 or even higher. It’s more like a graphing calculator and less like a LiteBrite.

One counterintuitive thing is it’s going to treat every matrix as if it’s square. The real width might be 30 pixels wide and 10 pixels high; in this case, if the code says, “draw a circle!” your matrix will display a squashed oval instead. When the rest of this starts making sense, you’ll also be able to understand when I say, “we can unsquash stuff using the X or Y scaling in the transform API”.