Here's a question -- Icosahedron shape with 10 pixels per strut (30 struts).

Here’s a question –

Icosahedron shape with 10 pixels per strut (30 struts). These are the cheap strips with 3 5050 LEDs per ws2811 chip. So it’s 900 LEDs, 300 pixels.

This was a demo for an larger installation that’s now finished and controlled by Madrix. The second picture is the patch used for this demo light. Madrix can do 3D patching but this ended up looking great, like an interrupted projection map of the globe.

So now it’s just a big weird light in my living room. THE QUESTION IS: can anyone think of a smart way to map this and mask bits so that I can write animations for rectangular arrays and run them from an arduino?

Even if someone has an example of this kind of masking with any other shape I’d be interested to see it. Thanks!

VERY cool shape and idea there!

I’d say there are two main approaches to coding in X,Y for this (versus coding to the specific 3-D struts, etc.).

First, you map it out like on the second picture and use code like we used for the RGBShades:

However, that code assumes that nearly every X,Y position has a pixel in it – with maybe a few holes. And that isnt’ what you have here.

So the second approach is: Have an underlying 2-d array of pixels, maybe 16x16, and draw all of your animation frames into that array. Let’s call that “the canvas”. Then you write a function that figures out which cells from the 16x16 canvas should be displayed on each of the icosahedron LEDs.

The simplest version of this would just be a big const remapping table so that you could:

…draw animation frame into the canvas…
…then…
for( int i = 0; i < NUM_LEDS; i++) {
// for each icosahedron pixel
// figure out which canvas cell
// it should display:
int j = remappingTable[ i ];
// copy the canvas data to the real leds
leds[ i ] = canvas[ j ];
}
FastLED.show();

A fancier version of this might sample multiple neighbor canvas cells and blend them together to figure out what to put on each icosahedron led. (That ‘oversampling’ can get quite sophisticated if you wish!)

Maybe 32x16. Or 32x32. Depends how much RAM you have on the device (eg It’s no problem on a Teensy 3.2 or ESP8266).

Mark –

These are both great, and they both solve the unnamed messy wiring problem (this polyhedron can’t be wired in one long unicursal line)

Im wondering if a bitmap like the shades program would have memory problems if the matrix was 30 x 60?

Or if the remapping table could have more than one LED addressed to the same bit in the canvas groups to make a rotationally symmetric effect?

The concern being that - with either of these approaches - maintaining the resolution on the horizontal struts is what really sells it.

Thanks very much!

Addendum: Anyone looking at this project for inspiration - if I could change one thing about this (and if I ever make another) I would face all the struts inward, thereby projecting geometric shadows on the wall and creating a hard line silhouette against the shape of the structure.

@J.D_Fontanella : Great thoughts and questions. You can totally have multiple icosaLEDs map to the same canvasLEDs, and do all sorts of interesting things that way, effects-wise.

The RGBshades-map approach uses as much RAM as if the whole thing was an X-by-Y array of LEDs. So 60x30 = 1,800 ‘virtual’ LEDs = 5,400 bytes of RAM just for the LED array – which is fine if you’re on an ARM (Arduino Due, Teensy 3, etc) or ESP platform. If you’re on an ATmega MCU, you’re out of RAM in a hurry.

There’s another approach which might be workable here: write a custom “XY” function that understands the topology of the struts. In fact, that might be the right way to go here. It would look something like this:

uint16_t XY( uint8_t x, uint8_t y)
{
uint16_t i = SAFETY_PIXEL_ID; /* minus 1 */
if( y < 10 ) { // top bars
uint8_t bar = x / 11;
if( (bar * 11) == x ) {-
i = bar + (y * 5);
}
} else if( y == 10 ) { // top horizontal strip
uint8_t bar = x / 11;
if( (bar * 11) != x ) {
i = TOP_STRIP_BASE_ID + (x - bar);;
}
} else if( y < 21 ) { // middle zigzags
…etc… {this will be a pain but maybe worth it}
} else if( y == 21 ) { // bottom horiz strip
…etc…
} else if( y < 32 ) { // bottom bars
…etc…
}
return i;
}

You define SAFETY_PIXEL_ID as minus 1, and define your leds array like this:

CRGB leds_plus_safety_pixel[ NUM_LEDS + 1];
CRGB const leds( leds_plus_safety_pixel + 1);

Now you can just do this:

leds [ XY( xcoord, ycoord) ] = color;

For any values for xcoord and yoord that do NOT map to a real pixel, the XY function will return negative 1. And leds[—1] is the same as leds_plus_safety_pixel[0], which is a perfectly legitimate place to write to - no crash, no problem.

Now, there are two problems with this:first, writing the XY function correctly will be a pain in the butt. I stopped my example code just before the really annoying part, but it’s still totally doable with a combination of patience, math, and a lot of if-then statements. You’ll also want to get all that “long division” and “modulo” out of there for speed reasons - a simple lookup table called “XdividedBy11” might help with that.

But the second problem with this approach is performance/waste.

Suppose you want to paint a rainbow wash over the whole thing. Since your programming model is just a 32x60(?) array, your code might do this:
for( y = 0 to 31) {
for( x = 0 to 59) {
leds[ XY( x, y) ] = rainbow color
}
}

And before you know it, you’re having to calculate and ‘paint’ nearly 2,000 virtual pixels every time – even though most of them don’t exist and are invisible and a waste of time. Wasteful and slow.

The more I think about this, the more I like the idea of having a canvas array of say 32x16 and mapping each icosaLED to one (or more, via supersampling) canvasLED. You can always increase or decrease the resolution of the canvas independently of the physical resolution of the icosahedron, to balance speed and memory and visual density etc.

Lots of ways to approach this, each with various (interesting / annoying!) trade-offs. Let us know what you try and how it goes!

@J.D_Fontanella I am with @Mark_Kriegsman . I use a very similar approach in all my projects(esp8266, rpi, mega, and nanos). I use a layout map of [x,y] coordinates maped to physical locations, and a [x,y] canvas that is drawn to. This solution, whilst not being very memory friendly, works for any surface type

@J.D_Fontanella I’ve been thinking of constructing something similar, but would ideally build a quick prototype first. How did you construct/wire yours? It looks nice and simple in your picture, but I couldn’t make out the details. Thanks!

You need an extruded aluminum profile that’s hollow, and then it all ties together sort of like this thing:

As Leonhard Euler spent his life examining: there is absolutely no easy way to organize the sides of any of the platonic solids. Starting from the top vertex we wired each strip radially, then ran wire back through the hollow of the same strut and hooked it to the strip next to it.

This has no fewer than 180 solder joints. And the cut edges of the struts are conductive. You also have to run power to strategic points to keep from suffering voltage drop. It is not for the faint of heart. You might try finding an ebay seller that will sell you pre-wired, shrink-wrapped strips cut to length. Then use wire nuts or crimp connectors. As for the vertices, just tuck the wires in as well as you can, no one will notice when it’s all lit up like Tokyo.