Shift Patterns - AlbertGBarber/PixelSpork GitHub Wiki

Overview:

A "shift pattern" is a pattern of colors that is moved either along segment lines or segments (depending on the effect). You can use shift patterns to draw complex shapes and move them across your segment sets. For example, you might use one of these effects to draw a flag, storing it as a shift pattern. Currently, only the Pattern Shifter (Seg Line) and Pattern Shifter (Seg) effects use shift patterns.

A shift pattern is a struct, shiftPatternPS, that contains an array, a length, and a pointer to a segment set. The array lays out the location and colors of a pattern, while the length is just the array's size. Note that the array is only 1D and has a very specific structure, which I'll explain below. Due to this structure, shift patterns will generally only work on a specific segment set. The struct's segment set pointer allows you to "bundle" the pattern and the set together, for ease of use.

Finally, note that a good understanding of segment sets is needed to understand shift patterns.

Creating Shift Patterns:

Creating a shift patterns is most easily shown with an example:

Lets say I have an 8x8 matrix and I want to draw an arrow shifting horizontally across it. Assume that I've already defined a segment set with 8 segments, each representing a horizontal line on the matrix.

We want our arrow to look like:

// "*" represent individual pixels
// "0" represent the arrow pattern we want to draw
/*
    0  *  *  *  *  *  *  * <- Seg 0
    *  0  *  *  *  *  *  * <- Seg 1
    *  *  0  *  *  *  *  * <- Seg 2
    *  *  *  0  *  *  *  * <- Seg 3
    *  *  *  0  *  *  *  * <- Seg 4
    *  *  0  *  *  *  *  * <- Seg 5
    *  0  *  *  *  *  *  * <- Seg 6
    0  *  *  *  *  *  *  * <- Seg 7
    |  |  |  |  |  |  |  |
    0  1  2  3  4  5  6  7 <- segment Lines
*/

I have called the segment set above horzLines.

The 0's in the above diagram represent the arrow we want to draw.

To create the arrow as a shift pattern I would represent it as:

//255 in the pattern indicates the background, but to keep the matrix easy to read, I define 255 as I
uint8_t I = 255; 

//Each line color pattern is numSegs long, it's easier to define an numSegs to plug in here
const uint16_t numSegs = horzLines.numSegs;

//The pattern array to store the arrow shape 
uint16_t arrowPattern_arr[(numSegs + 2) * 4] = {
    0, 1,   0, I, I, I, I, I, I, 0,
    1, 2,   I, 0, I, I, I, I, 0, I,
    2, 3,   I, I, 0, I, I, 0, I, I,
    3, 4,   I, I, I, 0, 0, I, I, I,
};
//  Part 1  ------ Part 2 --------
//Seg Lines --Line Color Pattern--

//Final part of setting up the shift pattern:
//Note that we include the segment set we intend to use the pattern with in the pattern definition
shiftPatternPS arrowPattern(horzLines, numSegs, arrowPattern_arr, SIZE(arrowPattern_arr);

The array above may look strange at first, but it is actually quite simple. While shift pattern arrays are one continuous array, they actually have distinct sections, which each represent a pattern for a segment line (or lines). To help show this (and make it much easier to see what a pattern is), I've split the array across multiple text lines. Each line represents a segment line in the 8x8 matrix and consists of parts 1 and 2.

  • Part 1 is ALWAYS two digits, representing the start and end segment lines for the line color pattern (part 2). This means that for patterns with repeating parts, you don't need to keep re-defining same pattern for each segment line, you can just increase how many lines each part of the pattern is draw over.

  • Part 2 is the line color pattern. It is almost ALWAYS as long as the number of segments in the segment set (numSegs) (unless otherwise noted in the effect), with each entry representing a palette color index (or 255 if it's to be background) for each segment.

Lets take the first the first line in the array:

    0, 1,   0, I, I, I, I, I, I, 0, 
// Part 1   ------ Part 2 --------
//Seg Lines --Line Color Pattern-- 

Part 1 is the start and end segment lines for the line color pattern represented by part 2. So the first pattern line starts at segment line 0, and ends at line 1, meaning that the pattern will be draw on one segment line: line 0 (the pattern is draw up to the last line, but does not include it). Lets say that we wanted to the color line pattern over two lines; we'd simply change the end line to 2, so the pattern would be draw on lines 0 and 1.

Part 2 is the line color pattern. It 8 has entries (the same as the number of segments in the segment set, ie rows in the matrix), with each entry representing a palette color index or background for each the 0'th line pixel in each segment (since the pattern only spans 1 line, each index corresponds directly to an individual pixel in the matrix). The background pixels are indicated by 255, but in order to make the pattern easier to read I've set a variable I = 255 and then used it in the pattern instead. So the line 0, I, I, I, I, I, I, 0, will be output as: segments 0 and 7 (the first and last segments) will be set to palette color 0, while the other segments will be set to the background color (which is set in the effects, not here).

The remaining lines in the pattern follow a similar setup, with each being 1 line long. All of them combine to create the arrow pattern we want.

The overall length of the array is (numSegs + 2) * 4. This should be similar for most other patterns: (numSegs + 2) is the length of each pattern "row", the "+2" spanning part 1 and numSegs spanning part 2, "*4" is the number of "rows" in the pattern.

Constructor Definition and Variables:

With all this info, we can create the shift pattern using its constructor:

shiftPatternPS(SegmentSetPS &SegSet, uint16_t PatRowLength, uint16_t *PatternArr, uint16_t Length);

Where:

  • SegmentSetPS* segSet -- The segment set we want to draw the pattern on. (horzLines in the example above).

  • uint16_t patRowLength -- How long each "row" of the pattern is. (part 2 of the array in the example above).

  • uint16_t* patternArr -- The full pattern array. (arrowPattern_arr in the example above).

  • uint16_t length -- The pattern array's length (which we use SIZE() to set automatically).

To create the shift pattern from the example above, we would do:

shiftPatternPS arrowPattern(horzLines, numSegs, arrowPattern_arr, SIZE(arrowPattern_arr);

With the pattern being called "arrowPattern".

Example Summary:

  • Shift patterns are usually specific to a segment set

  • For the pattern array:

    • Part 1 is how many segment lines the pattern line spans (up to, but not including the final line).
    • Part 2 is the color pattern on the segments. 255 is used to indicate a background color.
    • The length of the array is usually ( <numSegs> + 2 ) * ( <number of "rows" in the pattern> ). "rows" can vary in length, but are usually numSegs long (see the each effect for more info).

Final Gotchas:

  • The segment lines (part 1) don't have to be in sequential order, ie you can have a a pattern line from 3 to 4, then a line from 0 to 3 (this can be useful when working with odd shaped segment sets, where lines overlap).

  • Gaps between segment lines may cause weird effects (ie you have a pattern line from 0 to 2 and from 5 to 6, but nothing in between). Gaps won't be filed with anything, so the lines patterns will just repeat as the pattern moves.

  • Make sure you use the correct array lengths!

  • The direction the pattern moves is set by the segment set's segment directions. Changing the direction reverses the segments, and will in-turn reverse your pattern.

  • Patterns require a segment set as a input. This is tied to the pattern's segSet pointer, like in effects. You can change the segSet just as you would in effects, this will also change the segSet for any effects using the pattern.

shiftPatternPS Struct Info:

(see "shiftPatternPS.h" for code)

Settings:

  • SegmentSetPS segSet -- The segment set we want to draw the pattern on. (A constructor input).

  • uint16_t patRowLength -- How long each "row" of the pattern is (omitting the "+2", usually numSegs). (A constructor input).

  • uint16_t* patternArr -- The full pattern array. (A constructor input).

  • uint16_t length -- The pattern array's length. (A constructor input).

  • uint16_t numRows -- How many "rows" are in the pattern array. (Set automatically during construction).

  • uint16_t patLineLength -- How many total lines of the segment set the pattern spans, ie "from segment line 0 to 8". (Set automatically during construction).

Functions:

These functions help you transfer the shift pattern onto the segment set by doing the array traversal math for you. (You probably won't ever need to call these functions unless you're writing an effect using shift patterns)

  • uint16_t getPatRowStartIndex( uint16_t patternRow ); 

    Returns the first index of the specified "row" in the patternArr. ie, in the above example, the first index of the second row is 10.

  • uint16_t getLineStartOrEnd( uint16_t patternRow, bool isLineEnd ); 

    Returns either the start or end segment line for the specified "row" in the patternArr. isLineEnd = true will return the end line for the row.

  • uint16_t getLineColorIndex( uint16_t patternRow, uint16_t segNum );

    Returns the color index for the specified "row" in the patternArr at the specified segNum.

  • uint16_t getLineColorIndexQuick( uint16_t patRowStartIndex, uint16_t segNum ); 

    Returns the color index based on the "row" start index and the specified segNum. Is faster than getLineColorIndex() above, but you need to already know the "row" starting index ( found using getPatRowStartIndex() ).

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