Skip to content

FastLED 3.9.17 - stdlib compatibility, audio preview, xypath

Choose a tag to compare

@zackees zackees released this 24 May 22:12

New Project

This release has a few bug fixes in it and some internal refactorings that has been on my to-do list for a while. The next release will have more features and less core changes.

FastLED now has a small subset of std:: data structures. This allows complex code ingest from open source which rely on things like std::vector<>, hashmaps and others. Unlike other micro stdlib attempts, this one actually compiles everywhere thanks to our ~50 testing configurations that we run on each change committed to the repo.

These std data structures were used to create complex rendering functions xypaths that look absolutely jaw dropping.

However in this release my attention got pulled into about four different directions. Audio and xypaths were added to the core, but the examples were prunned for this release, in order to get this release out in a timely manner.

What's all the noise about lines and rasterization in this release?

You ever try to draw a point or a line on a LED matrix? By default it looks awful. As the point particle moves it lights up one led index at a time. It's tricky to make this transition look smooth. Drawing beautiful lines on matrices requires pixel-neighboring calculations in order to correctly blend into a low resolution strip/matrix. And that's what most of the new math you see below is about.. Take a point in float(x,y) and then color a tile of 2x2 pixels, or 2x1 if you are in a strip.

Happy coding!

Change list

  • esp
    • esp-idf v5.4 fixes to include lcd_50
    • RMT5 will now respect DMA_MODE=DMA_ENABLED
      • Default is still off.
      • #1927
        s.
    • datastructures
      • FastLED now has it's own subset of std lib. fl::vector<>, fl::hash_map<> etc so you can bring in external code to your sketches easily and have it still be cross platform compatible. Our std lib subset is backed by a fleet of platform testers so it compiles and works everywhere. Will this increase the AVR and other small memory footprints? No, we have strict checks for these platforms and compile size remains the same.
      • fl::hash_map
        • open addressing but with inlined rehashing when "tombstones" fill up half the slots.
      • fl::hash_map_inlined
      • fl::hash_set
      • fl::vector
      • fl::vector_inlined
      • fl::function<>
      • fl::variant<T,...>
      • fl::optional
  • graphics
    • CRGB::downscale(...) for downsizing led matrices / strips.
      • Essentially pixel averaging.
      • Uses a fastpath when downsizeing from M by N to M/2 by N/2.
      • Uses fixed-integer fractional downsizing when the destination matrix/strip is any other ratio.
    • CRGB::upscale(...) for expanding led matrices / strips, uses bilinear expansion.
    • XYPath (Work in progress):
      • Create paths that smoothly interpolate in response to animation values => [0, 1.0f]
      • Still a work in progress.
    • Subpixel calculations.
      • Let's face it, low resolution matrices and strips produce bad results with simple pixel rendering in integer space. I've implemented the ability for using floating point x,y coordinates and then splatting that pixel to a 2x2 tile. If a point is dead center on a led then only that led in the tile will light up, but if that point moves then other neighboring leds will start to light up in proportion to the overlap. This gives 256 effective steps in the X and Y directions between neightbors. This greatly improves visual quality without having to super sample.
    • Line Simplification
      • Take a line with lots of points and selectively remove points that
        have the least impact on the line, keeping the overall shape. We use an improved Douglas-Peucker algorithm that is memory efficient. We also have a version that is more cpu intensive which will will hit a target number of vertices.
    • RasterSparse: efficient rendering to an intermediate buffer that only allocates x,y points for values actually written, then flush to LED matrix/strip. See below for more information.
    • traverseGridSegment
      • Given a line A-B, find all the intersecting cells on a grid.
      • Essentially 2D ray tracing.
      • Great for optimization of particle trails and rastering an entire XYPath.
        • Example:
          • Full XYPath (e.g. Heart) renders 200 xy points
          • Use line simplification to reduce this to 50 most significant points -> 49 line segments
          • For each line segment
            • traverseGridSegment computes all the intersecting grid points
              • for each grid point find the closest point on the segment, call it closest-pt
                • closet-pt generates a tile2x2 of itself plus it's 3 neighbors
                  • for each tile2x2 it will have a uint8_t value representing it's intensity / closeness to center.
                  • tile2x2 list/stream -> raster (RasterSparse)
                  • raster -> composite to LED matrix/strip using a gradient or draw functor.
    • RasterSparse
      • A memory efficient raster that elements like the XYPath can write to as an intermediate step to writing to the display LEDs. This allows layering: very important for creating things like "particle trails" which require multiple writing to similar pixels destructively and then flushed to the LED display. For example if a particle has a long fade trail with say 30 points of history, then this entire path can be destructively drawn to the raster then composited to the led display as an unified layer.
      • "Sparse" in "RasterSparse" here means that the x,y values of the pixels being written to are stored in a hash table rather than a spanning grid. This greatly reduces memory usage and improves performance. To prevent excessive computation with hashing, a small 8-unit inlined hash_table with a FastHash function is carefully used to exploit the inherent locality of computing particle and paths.
      • Right now, RasterSparse is only implemented for uint8_t values and not an entire CRGB pixel, as CRGB values are typically computed via an algorithm during the compositing process. For example a gradient function can take a rasterized particle trail and apply coloring.
    • LineMath
      • Take a line A-B and calculate the closest distance from the line to a point P. This is important to optimize rendering if oversampling takes too much CPU.