Skip to content

Mapping Concepts

Finn Andersen edited this page Nov 3, 2023 · 7 revisions

This section is for some more in-depth explanation of the concepts of linear pattern interpolation, and spatial pattern coordinate system mapping.

Linear Pattern Mapping

The length of the pixel array that a LinearPattern applies its logic to gives it an effective "resolution", which can be different to the length of the StripSegments the pattern is to be mapped to.

Using more virtual pixels than the number of LEDs in a strip segment allows for smoother pattern animations. For example, consider a simple pattern of a pulse of light moving along a 1-dimensional axis. The pattern logic will need to keep track of the position of the light pulse, and increment it on every new frame. If the resolution of the pulse position was limited to the number of LEDs on a segment, then the animation won't look like a smooth movement but a jerky jumping from one LED to the next with nothing in-between. Additionally, if the segment was only 20 LEDs long, and you wanted your pattern to run at 50FPS, the pulse would travel the entire length of the segment in 0.4 seconds, which is probably much faster than you would like.

Assume the pattern is a simple pulse of length 4 moving along a linear axis with 11 virtual pixels (pattern resolution). This pattern needs to be mapped to an LED strip segment which only has 5 LEDs. The below diagram shows the pulse as filled in pixels on the virtual pattern axis, and what the corresponding LEDs should display to best represent the pulse.

The LED values will be calculated as following (assuming a filled pattern pixel has a value of 100):

  • LED #1: Calculated as the average of the entire first 2 pattern pixels, and 1/5th of the 3rd pixel: (1*0 + 1*0 + (1/5)*100) / (1 + 1 + 1/5) = 9.09
  • LED #2: Calculated as the average of 4/5ths of the 3rd pixel, the entire 4th pixel, and 2/5ths of the 5th pixel: ((4/5)*100 + 1*100 + (2/5)*100) / (4/5 + 1 + 2/5) = 100
  • LED #3: Calculated as the average of 3/5ths of the 5th pixel, the entire 6th pixel, and 3/5ths of the 7th pixel: ((3/5)*100 + 1*100 + (3/5)*0) / (3/5 + 1 + 3/5) = 72.72
  • LED #4-5: Calculation follows same pattern, both will have value of 0

This interpolation functionality allows for flexible mapping of patterns to LED strips and will cause the animation to appear smoother. There is a tradeoff between animation smoothness obtained from higher resolution values, and increased CPU and memory utilization. The interpolation algorithm is faster when the number of virtual pixels is an integer multiple of segment length.

Some patterns will not benefit from having a higher "resolution" than the number of physical LEDs in a segment, such as:

  • 'discrete' style patterns
  • those that do not involve movement
  • Those that use trigonometry functions to generate wave-style effects (they are already "smooth") In these cases, it's best to have the number of pixels equal to strip segment length for a 1-to-1 mapping.

Spatial Pattern Mapping

Spatial Pattern Mappings follow a similar analogy as Linear Patterns, in that there is a 'virtual' 3D space in which a SpatialPattern is defined, and a different 'real' 3D coordinate system in which the physical project's LED strip segments exists.

Project Coordinate System

If you want to use SpatialPatterns, it's up to you to define a coordinate system (origin position and scale) for your project. Say we wanted to add one to the LED arrangement from the tutorial, by putting the origin in the middle and coming up with some appropriate scale so that ends of strip segments will have integer coordinates (just for convenience).

Note this example is 2-dimensional so only X and Y coordinates need to be considered. The same logic applies for a 3-dimensional project. The below coordinates are not exact but a rough approximation.

  • Segment 1 (LEDs 0-7) goes from [10,-5] to [10, 5]
  • Segment 2 (LEDs 8-21) goes from [8, 7] to [-8, 7]
  • Segment 3 (LEDs 22-29) goes from [-10, 5] to [-10, -5]
  • Segment 4 (LEDs 30-43) goes from [-8, -7] to [8, -7]

SpatialStripSegment is initialised with a StripSegment instance along with its start and end position in the coordinate system. Using the StripSegments defined in the tutorial, the SpatialStripSegments would be defined like:

SpatialStripSegment spatial_segment1(segment_1, Point(10, -5), Point(10, 5));
SpatialStripSegment spatial_segment2(segment_2, Point(8, 7), Point(-8, 7));
SpatialStripSegment spatial_segment3(segment_3, Point(-10, 5), Point(-10, -5));
SpatialStripSegment spatial_segment4(segment_4, Point(-8, -7), Point(8, -7));

The spatial coordinates of all the other LEDs on the strips will automatically be calculated by assuming the strip runs in a straight line and the LEDs are evenly spaced.

Mapping and Scaling

As described in Reference, SpatialPattern has a resolution parameter which specifies the scale of the 3D cube the pattern operates in (defaults to 100 which means a 200x200x200 cube centered around the origin). The SpatialPatternMapping handles the mapping of this pattern space to the project coordinate system defined above. This allows the spatial pattern to be stretched and positioned as desired. This is done using two parameters:

  • scale_factors: Scaling vector to apply to a pattern bounds to scale it onto the project space. Each component represents the scale factor in that dimension. If not specified, is automatically calculated to scale the pattern bounds to align with the project space.
  • offset: Translation vector to move the pattern bounds before scaling. If not specified, is automatically calculated to move the pattern area so that its centre and that of the project space is aligned.

For the above 2D example, the project space bounds are (-10, -7) and (10, 7) which corresponds to a 20x14 rectangle. In order to map a 200x200 2D spatial pattern onto this space, the transformation components would be automatically calculated as:

  • scale_factors: Calculate factors to scale the 200x200 square to the 20x14 rectangle: (20/200, 14/200) = (0.1, 0.07)
  • offset: Both the project space and pattern space are already centered at the origin so no translation is required to align them. (0,0)

These are the transformation parameters that would be automatically calculated if none were specified to the SpatialPatternMapping. However, since the X and Y components of the pattern space are scaled by different factors, this would cause the aspect ratio of the pattern to change. If this is undesirable, you may want to manually specify the scale_factors as (0.1, 0.1). In this case, the aspect ratio of the pattern would be maintained, however some of the content from the top and the bottom would lie outside the project bounds and not be visible.

Linear to Spatial Pattern Mapping

A linear pattern can be projected along a vector direction in a spatial LED configuration. The pattern pixel applied to each LED is determined by the LED's distance from the perpendicular plane at the start of the vector path. By default, the linear pattern path will start on the edge of the projects bounds and move through it in the direction of the vector and end at the bounds on the other side. The start position and length of the path can be adjusted using the offset and scale parameters.

Since the pattern pixel for an LED is determine by its distance from the start position, be default the effect will be mirrored about the start of the vector path. If start position is outside the bounds of the LEDs, then this will not make any difference. Otherwise, this can be disabled by setting mirrored=false (with an extra performance cost)