Pentagonal icositetrahedron

Ohh polar?? :thinking: Can you suggest some specific patterns using polar coodinates so I can look at some code? Or even 3d patterns that do rotational things outside y,x,z axis? Or suggestions for ring patterns might work?

I was a bit surprised there aren’t any sphere patterns. I know it’s a niche but led “balls” of various geometry aren’t that unusual in the led maker community. I suppose folks are rolling their own art and not relying on things like pixelblaze that have premade patterns. Pity.

So uniform cartesian and polar patterns are nice but they ignore the unique structure and one of the things I found in spending lots of time writing patterns for low pixel count rings is that patterns that are specific to the structure have a lot of appeal versus just treating it as a generic “ring”. This is a challenge with low face count polyhedra like dodecahedron (D12) and icosahedron (D20) that is shared by the infinity cube type builds as well, especially non-uniform ones like the cubocahedron (D14, squares and triangles).

So this shape does have some interesting symmetry. A symmetric four face vertex:

And a symmetric 3-face vertex:

There’s also 3 kinds of asymmetrical 3-face vertices.

One of the things I’d like to explore is mapping the led faces to a network structure and writing patterns that rely on network traversal or network symmetry. This is a bit challenging with a neutered language like pixelblaze that doesn’t support classes and structures. Anyway you can do it all with arrays and global functions, it’s just a lot more work and a bit of an archaic approach from a programming perspective.

Onward! :grinning_face_with_smiling_eyes:

1 Like

More later, but @jeff did a nifty Polar Star that will likely inspire you a bit.

I’m not sure we have any patterns using 3D polar, which is radius, angle aka theta (th), angle2 aka height angle(ha)…

But the beauty of PB mapping is that you could map that way, and treat x,y,z as r,th,ha and so long as your pattern makes sense and does the math right, it’ll work as expected.

I need to finalize one of my 3D sphere builds, life’s put it on the back burner, but we absolutely have enough people interested to make patterns for it.

Also, re writing for a specific size/ring versus generic… Yes, there are much better results if you tune for the exact size/count. However, if you make generics that allow you to use sliders to play with, and then make specific patterns using the best results found while “sliding”, that’s the best of both worlds.

If I understand, you’re talking about the global map singleton edited in the web UI?

If I decided to model this using that in polar coordinates it means every pattern needs to be one that makes the same assumption which probably isn’t going to work I guess.

Something to investigate more.

There are a few that calculate the angle and radius, and Glow Flow does 3D coordinate transformations based on the accelerometer direction.

You can use the 2 or 3 coordinate data for almost anything you want, though it will be scaled to world units. For broad compatibility, the default and supported coordinate system is Cartesian, with helper functions and utilities built on that assumption.

Since you can get angles from those I don’t see a huge benefit of using polar coordinates as the pixel map outside of a performance benefit, which I hope to minimize with additional tools/APIs.

In some cases it might be easier to make the map from polar/spherical coordinates, and maybe an interesting feature to be able to specify the map in those terms.

One if the appealing features of Pixelblaze is that patterns can be written to work on a broad range of physical LED installations. That doesn’t preclude doing piece specific things, but I mention it because that is where I intend to add features and has broad appeal for sharing patterns.

I’m currently mid way with 3.17, and have upgraded pixel maps to 16 bit, and am evaluating matrix and coordinate transformation libraries that would allow you to apply transformations on the pixel map coordinates with native performance.

I think polar and spherical coordinates make sense for a lot of pieces/patterns and are a fun way to think about patterns. Perhaps sometime in the future there could be a renderSpherical(r, a, p) renderer that could get radius, azimuth angle, and polar angle. The engine could do coordinate system conversions automatically.

1 Like

@aaroneous

I’d love to understand a bit more about what you can with this kind of thing:

I can see how this would be very nice for many effects. How do you see modeling that?

It’s it not the same, but I would say that you can get some interesting effects just using triangle() between segments. Eg if you had 10 pixels between each joint/corner, something based on triangle(index/10) will have symmetry, and can be expanded with additional effects.

Yeah, that would be interesting.

The problem I see now is purely a math one:

If you put in x,y(,z) coordinates, and you want to use polar, you have to do the math, and in most cases, you do the math repeatedly, for each pixel, each render. If you do the map as polar, the polar patterns are easy, but using Cartesian patterns (the vast majority) requires doing the reverse math. Yes, @aaroneous that’s the current problem, you can only map one way OR the other at a time. Swapping maps isn’t easy.

@wizard, the math itself is trivial, but spending cycles each render to re-calc is the problem.
Solutions: allow multiple maps (with some way to decide which map to use), OR prerender and/or cache, so it’s all available? So if I enter an x,y,z map, but a given pattern wants to use renderSpherical, behind the scenes, the PB does the math, and caches it, so it’s not doing it over and over?

Otherwise, on the fly is so expensive, it’s a sqrt of coordinate squares added, plus an atan2(y,x) (plus whatever the math is for the HA, I think that’s another atan2).
added: according to Spherical coordinate system - Wikipedia
HA is actually atan2(r,a)…

If we can make PB “just do it behind the scenes” that’s the true win.

Building a “Cartesian 2Polar” function and vice versa is easy…if we could cache that, so it’s all done only once, that would be awesome.

Hmm, thinking of prepopulating into an array now… (Inspired by the below). “Prepopulating” isn’t possible, because we don’t have the pixel info (yet), but we could one-time (first time thru render) setup the array of polar data, and then it’s available after that. I’ll try this later this week.

Off the top of my head…

Each pixel index has an array of five elements that contains the indices of the adjacent pixels, perhaps it also has a single piece of data which indicates the two faces that are longer.

EDIT: I realized that the first two elements can be implicitly considered the longer edges.

Now you can play around with snake effects that walk between faces in some order or randomly, or cellular automata algorithms which are moving between discrete panels rather than being mapped from a 2/3 dimensional cartesian grid. With the info about which side is longer you can easily write functions to spin animation in each of the symmetric 4 face elements, clockwise or counter clockwise, perhaps moving between the 6x 4-quads in some interesting way.

2 Likes

So you could build that now, right?
var data = array(pixelCount)

Then loopthru each pixel, and add an array to data[index] = array(5)
Etc .

The problem is (was?) more that array functions were scarce… I think the new functions could be used to parse this, once it’s put in the right format.

@Scruffynerf, I’ll look at optimizing the performance after the pattern code interface/design. Design is top priority. The cost may be trivial (compared to all the other stuff going on), or could be optimized with a cache. Even doing conversion in pattern code now isn’t a deal killer, and anything I add to the engine will most likely be much faster than pattern code.

Example: Firework nova on my 8x8x8 cube gets 46.5 FPS while calculating radius manually with sqrt(x*x + y*y + z*z). I’ve since added hypot3 which needs a code change to take advantage of (and hadn’t bothered to until now). If I switch to hypot3(x,y,z) It goes to 50 FPS, +7%. If I swap that out with x or something as though the radius was from the pixel map or a cache, I get 53 FPS, +6%. It’s a nice little bump, but not enough to make or break the pattern.

However, if I was using API calls like radius(), etc., or wrote the pattern using a renderSpherical then the implementation would be even easier, and could get faster with zero pattern code changes as optimizations to the engine are made. Plus anyone with a standard pixel map can run it too.

1 Like

Sounds like it’s time for a Polar library.
Damn, our library shelf keeps growing.

1 Like

@aaroneous, I really like this build and look forward to seeing what you do with it. I’m with @scruffynerf here - since the radius is essentially fixed, full 3D is not really necessary. I think I’d first play with a 2D spherical mapping. The network idea would be fun too.

Coming tomorrow - small update to Pixel Teleporter so folks can experiment with mappings/patterns for this sort of shape without having the physical object at hand.

I’m being Mr. Lazy Geometry Person at this point and just supplying some basic tools. A full on Archimedean/Catalan solid library would be awesome, but it’s some distance down the project list right now.

2 Likes

As long as we’re dreaming, how about defining “PixelTeleporter” as an LED type within PixelBlaze (I realize this will need driver code behind it) so we don’t need a second physical device to read and transport the pixel data for viewing?

1 Like

Are there any patterns that come to mind that wrap around the x or y axis? Treating this like a cylinder is probably a quick and dirty way to see how the matrix animations behave on a sphere, if I can find wrapping ones so there isn’t a “seam” :joy:

I took a swing at modeling the 6 quad vertexes as a list of 4 LED in clockwise order and then writing a simple chase pattern on each quad using triangle for brightness. The 6 quads form an octahedron of 8 triangles which allows for a triadic color scheme for each 3 quad pair. Convenient!! :joy: Triadic color schemes can be really vibrant and sometimes provoke feelings of curiosity because of how unusual they are especially on secondary or tertiary hues. This one spins thru the color wheel so you get to briefly see triadic color schemes for primary, secondary, and tertiary colors - just wait for them to come up in the video.

3 Likes

So, having played with the model for a while last night, I can see the issue more clearly, and understand your frustrations better. You’re right, and your most recent video makes the case - custom, one-off patterns are a great way to go for low pixel count devices.

Having said that, here’s a first cut at spherical mapping. First, the pattern, which circles a red dot around one axis at the center of the figure.

From the pattern’s point of view, the map is just normalized 2D x/y coordinates. (For distance calculations, you’re still going to have to use spherical or angular distance rather than pythagorean though. For only 24 pixels, that shouldn’t slow things down much.)

var frameTime = 999;
var xVal = 0;
var tolerance = 0.08;

export function beforeRender(delta) {
  frameTime += delta;
  if (frameTime > 100) {
    xVal = (xVal + .1) % 1;
    frameTime = 0;
  }
}

export function render2D(index,x,y) {
  h = 0.6667; // blue
  v = 0.2;
  if (abs(y - 0.5) <= tolerance) {
    if (abs(x - xVal) <= tolerance) {
      h = 0;  // red
      v = 1;
    }
  }
  hsv(h,1,v);
}

And here’s my mapping function. It includes the 3D points at the center of each face, which I then transform to azimuth/elevation angles, and give to the pixelblaze as a 2D map. (In my model arrangement, face 0 is facing towards the viewer, and the faces wind clockwise and away. )

// 2D azimuth/elevation map
function (pixelCount) {
  var coords = [[-2.6010864, 3.0588899, 371.27658  ],
[258.34598, 3.0588868, 266.66534   ],
[110.420815, 230.1553, 269.62604   ],
[-168.1251, 192.08896, 269.62604   ],
[-244.26431, -68.03337, 271.23572  ],
[-44.53873, -254.49617, 266.66534  ],
[216.40833, -254.49617, 162.05411  ],
[363.33734, -71.996475, 25.785542  ],
[303.80084, 196.81091, 82.661514   ],
[92.82195, 358.9328, 20.340021     ],
[-185.72397, 320.8665, 20.340015   ],
[-345.73846, 110.06282, 78.82567   ],
[-239.0621, -272.9436, 78.82567    ],
[8.652209, -370.18512, 27.395233   ],
[232.8638, -268.22162, -108.13883  ],
[280.9998, -33.930122, -240.31284  ],
[221.46329, 234.87724, -183.43689  ],
[-39.336555, 303.81482, -209.78938 ],
[-281.14703, 163.78499, -178.86655 ],
[-340.5363, -94.847404, -113.584366],
[-157.56064, -282.8001, -181.82722 ],
[66.650955, -180.83662, -317.3613  ],
[67.33978, 87.6523, -354.46417     ],
[-174.47067, -52.377552, -323.54135]]

  var map = []

  for (var i = 0; i < 24; i++) {
    r = Math.sqrt(coords[i][0] * coords[i][0] +
                  coords[i][1] * coords[i][1] +
                  coords[i][2] * coords[i][2])
    theta = Math.atan2(coords[i][1],coords[i][0])
    if (theta < 0) theta += 6.2831
    phi = Math.acos(coords[i][2] / r)
    map.push([theta,phi])   
  }
  return map
}
1 Like

Thanks for dabbling. This should give me some things to kick around!! Looks like there will be some caffeine and late nights in my future.

I’m not remotely concerned about fps with 24 pixels, which gives me some freedom to implement different patterns in different coordinate systems, and if the pattern is non-cartesian just hardcode a second map structure within each pattern. It’s not terribly portable but this is a unique structure that will have non-portable patterns anyway. I can write a python script or something to calculate coordinate and just copy/paste a block structure into each pattern.

So going to higher pixel counts “solves” some of these issues but also just makes it a generic sphere. Which is still delightful. But tosses a bit of the unique geometry out the window.

I have two short term options to solve this for the next project - each face should have 5 pixels instead of one with interior 3d printed walls for isolation. Then I have 24x5 = 120 leds in a sphere. And each face of the pyramid is a unique color, instead of the entire pyramid.

Or, I can go for the pentagonal hexecontahedron, 60 faces, 1 led per face, or 5 led for face with pyramids and 60x5 = 300 led. This probably eliminates the possibility of portable and power unless I go with very low brightness, but maybe I want to do that anyway - who wants 300 fully lit LED in your face? It’s essentially the same tradeoff as higher density led strips. What you sometimes want is the smooth animation not the higher brightness.

I’ve only been fiddling with this code for 5 days and my wife just asked why the 3d printer isn’t busy making noise and what is the next structure going to be. :rofl::rofl::rofl: She’s such a jerk sometimes.

EDIT: Incidentally, the 192 led sphere that Chris made here: 3D printed ball - Show and Tell - ElectroMage Forum shows the point at which higher density LED polyhedra start to look and behave like a sphere from a pattern perspective. I guess it’s somewhere between 100 and 200 leds. You can still see in Chris’ build that lines are choppy and low-res.

1 Like

Ironically this sort of “tolerance” check is one of the things I’m planning to add to my “generic” library list, to make it a single true/false function.
Used often and it’s an awkward construct (double ifs usually or sometimes &&)

I was thinking of within(x, low, high), as a clearer naming though, but tolerance is absolutely what it is, so maybe two different functions, one with one value [tolerance], one with a range[within], to allow asymmetrical ranges.

I read this if(abs) idiom as 1 dimensional hit testing. 2d and 3d hit testing is interesting and useful too! Both as a radial distance and as a boundary rectangle or cube. Rectangles are nice because you avoid costly multiplication and square root. Squares are even better. And for most led resolution is fine.

@zranger1 code sure looks like a hittest to check if (x,y) is within a square of w,h = 2*tolerance centered on (0.5,xVal), but I come from a graphics/UI toolkit background where this kind of thing is common:

if (hittest(point, rect))

Now… you don’t have structures in blaze-js, but basically that is what is happening here I think?

if (hittest(x, y, xVal-tolerance, 0.5-tolerance, tolerance * 2, tolerance * 2))

where params are x, y, rectX, rectY, rectWidth, rectHeight

idk if this is any more comprehensable lol and often you don’t want a binary if, but rather some variable like brightness based off distance. :thinking:

1 Like

RE: Comparison tolerance: It is indeed hit testing here. This idiom is also commonly used as 1/2 stroke width for drawing resolution independent figures in the render2D/3D() functions. And as @aaroneous mentioned, if you keep the value and scale pixel brightness by decreasing distance, it can also provides reasonably good antialiasing.

It’s also common to hook it to a UI slider so the user can adjust line thickness for their display as needed.

1 Like