Pentagonal icositetrahedron

I finished a 3d printed 24 led pentagonal icositetrahedron last weekend and my first pixelblaze came in Friday. Spent Saturday kicking the tires and today mapping the coordinates for the faces and writing my first pattern - complementary colors at opposite ends of the sphere rotating thru the rainbow with an rgb blend between. I’ll be dabbling in some 3d transforms tomorrow to randomly rotate the hemisphere plane thru the geometry which should make for a nice pleasing effect. And then a single white pixel that spins around the equator? Not sure. Any tips for a new would be appreciated as I’ve never done 3d math before.

Fun board and ecosystem to play in.

Hopefully this youtube video link works:

The piece is called “Take me to your leader”. With only 24 LED and not arranged in a symmetrical grid this will be best suited to “low res” patterns. A fun challenge.

4 Likes

Beautiful piece. I like it’s simplicity. Are you going to post any construction details?

Not much to the build.

The pentagonal icositetrahedron is a 24 sided polyhedra whose faces are a stretched pentagon. Each face is 3d printed as a hollow pyramid with a bottom that has a hole for the LED.

The faces are 1mm thick PLA which is a nice diffusion for these lights. The print used 0.6kg of filament, about 1.4 pounds. I’m using a 0.6 nozzle, 0.2 layer height, each pyramid took 2.5 hours to print, so 60 hours for the entire project. It’s 15" or so in diameter.

I used ws2812 “bullet” lites that snap into the inside of the pyramid pointing out. The faces snap together from the inside and both the lights and 3d printed pieces are hot glued from the inside to make it more solid. The last face is a hollow ring with a removable hollow pyramid held in by magnets that allows access to the inside where the pixelblaze and USB brick live.

Mapping the LEDs was a huge pain in the butt. I finally ended up pulling the face coordinates out of a blender file of the polyhedra because I was too lazy to write the math to calculate the x,y,z.

All the geometry is described on the wiki page: Pentagonal icositetrahedron - Wikipedia

Once it’s mapped it looks like any other low-res sphere to pixelblaze. So I’ll use render3D for most of my animations. Although some of the more random canned patterns look ok if they aren’t assuming a high resolution strip, in general most of the patterns aren’t great.

I’ll be tackling a pentagonal hexecontahedron next which has 60 faces using the same construction technique. That ball will be twice the size, about 3 feet across.

3 Likes

Very nice. Based on the clear color separation, I figured it was hollow pieces, nice to see the build.

The 3D map aside, since this is equal all the way around, anything that does polar style mapping/patterns will look good. You want the round patterns, even if you light it up with “fake 3D” by taking the two and just using those coordinates.

We need more “spheric” patterns, so welcome to the club!

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