2D Segment Sets for 2D Effects - AlbertGBarber/PixelSpork GitHub Wiki

Overview:

In this example we'll go over how to create 2D Segment Sets and how they work with effects. This page is intended to be paired with the "2D_Segment_Sets_for_2D_Effects" library example code. Note that this example builds on the previous examples in the "Starter Guide" at the top of the right-hand wiki sidebar, so I won't be going over anything that's already been covered in a previous example.

In the example, we'll create two different 2D Segment Sets by virtually splitting the strip into multiple sections. We'll use the 2D version of the "Rainbow Cycle" effect from the previous "Basic Setup" example, but we'll swap between the two segment sets periodically, which will change how the effect is drawn on the strip.

Example Output for a Strip of 60 LEDs, Split Into Two Equal "Rows" Using Segments
Streamer effect on 2D disk

Note: when compiling using the Arduino IDE, if you have your "compiler warnings" (found in "preferences") set to "More" or "All" you may get a few warnings when you first compile a sketch. These should mainly concern the possible non-usage of various static variables, and are expected. They will not prevent the code from running!

What are 2D Segment Sets?

Simply put, any Segment Set with more that one Segment will be treated as a sudo 2D matrix, with the Segments acting as the matrix rows. For instance, in this example, we will split your LED strip in half, creating a Segment for each half. When we form the two Segments into a Segment Set, we are actually forming matrix with dimensions "2 x (NUM_LEDs/2)" (rows x columns, NUM_LEDs is your strip length). However, unlike a strict matrix, Segment Sets don't need to have equal length Segments. In other words, we could create a Segment Set where we have one Segment that is 1/2 the strip length, and two Segments that are 1/4 the strip length. The code automatically maps from the longer Segments into the shorter ones so that effects line up. Overall, this allows you to easily create virtual 2D shapes by simply organizing sets of LEDs as you see them IRL, without having to squeeze them into a perfectly even matrix.

Most effects are automatically setup to handle 2D Segment Sets, and will be drawn in 2D if given a 2D Segment Set. You can quickly check how an effect handles 2D by looking at its tags in the "Effect List" in right-hand wiki sidebar. You can read the specifics here, but basically any effect with a Seg Line or Seg tag will be 2D. Most effects draw using "Seg Lines", which means the effect draws along the Segment Set's matrix "columns" (lines), usually using a single color per column.

You can read more about 2D Segment Sets here.

The Segment Sets in this Example:

In this example we'll be creating two 2D Segment Sets: one where we split your strip into two equal halves, and a second where we split the strip into one half Segment and two quarter Segments. Note that, to get the most from this example, (if possible) you should fold your strip in half, so that it makes a "2 x (NUM_LEDs/2)" matrix. I know that won't be possible with every strip of LEDs, so I'll try to describe the outputs as best I can so that you can contextualize what you're seeing. If your strip is already in a 2D shape, such as a disc, square, etc, I encourage you to try to form it into a 2D Segment Set and test it in this example.

Note that if your strip has an odd number of LEDs, the Segment Sets below will omit the final LED, leaving it blank.

Segment Set One: Two halves:

The code below defines our first Segment Set, which splits the strip into two equal halves. When we run our Rainbow Cycle effect on this Set, the rainbow will be mirrored on the halves. If you have your strip arranged like a matrix, the effect will shift a rainbow across the matrix.

//The first half of the strip, setup using a continuous section that starts at 0, and is NUM_LEDS/2 long
const PROGMEM segmentSecCont halfSec0[] = { {0, NUM_LEDS / 2} };
SegmentPS halfSeg0 = { halfSec0, SIZE(halfSec0), true };

//The second half of the strip, setup using a continuous section that starts at NUM_LEDS/2 and is NUM_LEDS/2 long
const PROGMEM segmentSecCont halfSec1[] = { {NUM_LEDS / 2, NUM_LEDS / 2} };
//Note that we specify the direction of the segment to be false, this mirrors the segment from halfSeg0 above
SegmentPS halfSeg1 = { halfSec1, SIZE(halfSec1), false };

//Create the segment set using both segments (named halfSegSet)
SegmentPS *halfSegs_arr[] = { &halfSeg0, &halfSeg1 };
SegmentSetPS halfSegsSet( leds, NUM_LEDS, halfSegs_arr, SIZE(halfSegs_arr) );

Hopefully the code above is easy enough to follow, but in words: the Segment Set, named "halfSegsSet", is formed from two Segments, each having one Section that is half the strip. The first Segment starts at 0, and runs NUM_LEDs/2, where NUM_LEDs is your strip's length. The second Segment starts at NUM_LEDs/2 and runs NUM_LEDs/2, to the end of the strip. Note that the second Segment is opposite in direction (set by the false flag in the Segment definition) from the first Segment. This will reverse the direction of the rainbow effect on the second Segment, so that it ends in the center of the strip, "mirroring" the effect.

Segment Set Two: A half and two quarters:

The code below defines our second Segment Set, which splits the strip into three Segments, one that is half the strip, and two that make up the remaining two quarters. This Segment Set demonstrates how effects map onto Segments of differing lengths. When we run our Rainbow Cycle effect on Set, the rainbow will be repeated on each Segment, automatically mapping from the larger to the smaller Segments so that the rainbows stay in sync. In other words, the rainbows will all "start" and "end" at the same time on all the Segments (follow a single color to see this clearly). The second quarter segment has its direction reversed, so it mirrors the first quarter, acting like a mini version of the halfSegsSet above. This kind of automatic mapping and syncing is true for all 2D effects. For more examples, see Ring Segments page.

//Create the first quarter segment using a continuous section, starting at NUM_LEDS/2, with length NUM_LEDS/4
const PROGMEM segmentSecCont quartSec0[] = { {NUM_LEDS / 2, NUM_LEDS / 4} };
SegmentPS quartSeg0 = { quartSec0, SIZE(quartSec0), true };

//Create the second quarter segment using a continuous section, starting at NUM_LEDS * 3/4, with length NUM_LEDS/4
const PROGMEM segmentSecCont quartSec1[] = { {NUM_LEDS * 3 / 4, NUM_LEDS / 4} };
//Note that we specify the direction of the segment to be false, this mirrors the segment from quartSeg0 above
SegmentPS quartSeg1 = { quartSec1, SIZE(quartSec1), false };

//Create the segment set using the three segments, reusing halfSeg0 from the first segment set
SegmentPS *quartSegs_arr[] = { &halfSeg0, &quartSeg0, &quartSeg1 };
SegmentSetPS quartSegsSet( leds, NUM_LEDS, quartSegs_arr, SIZE(quartSegs_arr) );

The code above forms the half and quarters Segment Set, named "quartSegSet". Note that we are re-using the first half Segment, "halfSeg0" from the previous Segment Set since there is no reason to re-create it. The two quarter Segments are placed one after another after the half Segment. The first quarter starts at NUM_LEDS/2 and runs NUM_LEDS/4, while the second starts at NUM_LEDS* 3/4 and also runs NUM_LEDS/4. The second quarter has its direction reversed, so it mirrors the first quarter.

The Rainbow Cycle Effect:

To create our rainbow cycle effect we do:

RainbowCycleSLSeg rainbowCycle( halfSegsSet, NUM_LEDS / 2, true, false, 80 );

The code above creates our "rainbowCycle" using the RainbowCycleSLSeg effect, details here. RainbowCycleSLSeg moves a fixed length rainbow across the Segment Set, just like RainbowCyclePS from the "Basic Setup" example. However, unlike RainbowCyclePS, RainbowCycleSLSeg draws the rainbow in 2D (as indicated buy the "SL" and "Seg" in the effect name), configurable to either be along Segment Lines, or whole Segments.

In our case, we have configured the effect to:

  • Use our halfSegsSet for drawing (will switch between it and quartSegSet during runtime).
  • Use a rainbow length of NUM_LEDS/2, so that one whole rainbow fits on our half strip Segments.
  • Moves the rainbow in the positive direction along the Segment Set (direction is true).
  • Draws the rainbow along Segment lines (our Segment Set's matrix columns), each line will be a single color. (The effect's segMode is false).
  • The rainbow moves with an update rate of 80ms.

This will work well with both our Segment Sets, but I encourage you to experiment by changing some of the effect's settings!

Our Loop() Code:

Finally, we get to our actual runtime code. The code is fairly simple, we constantly update() our Rainbow Cycle effect, but every 10 seconds (handled by the FastLED EVERY_N_MILLISECONDS() macro), we swap what Segment Set we're using in the effect. We track the swapping using a bool, flipFlop. Changing the Segment Set changes how the effect is drawn. It automatically adjusts to the current Segment Set (note that for some effects, you'll need to call a function when switching Segment Sets, this will be explained on the effect's wiki page).

void loop() { 

    //every 10 sec, switch the rainbowCycle's segment set between quartSegsSet and halfSegsSet.
    //(note that the rainbowCycle effect allows us to change the segment set freely,
    //but for some effects, you may need reset() them when doing so. The effect's wiki page will explain)
    EVERY_N_MILLISECONDS(10000) {
        if( flipFlop ) { 
            //Change the effect's segment set, note the use of &, this is because the effect uses a pointer to the segment set.
            rainbowCycle.segSet = &quartSegsSet; 
            Serial.println("Using quartSegsSet now");
        } else {
            //Change the effect's segment set, note the use of &, this is because the effect uses a pointer to the segment set.
            rainbowCycle.segSet = &halfSegsSet;
            Serial.println("Using halfSegsSet now");
        }
        flipFlop = !flipFlop;
    }

    //update the rainbow cycle to draw it
    rainbowCycle.update();
}

Outro:

This example gave you an intro into 2D Segment Sets and effects. Segment Sets give you the ability to virtually arrange your LEDs into 2D shapes, and most effects automatically configure and map themselves to fill the Segment Set in 2D. Combined, this allows you to get very creative with your LEDs. For more worked 2D Segment Set examples, see the Ring Segments examples.

In the next example, Palettes and Util Classes, we'll go Pixel Spork's palettes, and how to use utility classes to modify you effects.

⚠️ **GitHub.com Fallback** ⚠️