/ adafruit_fancyled / fastled_helpers.py
fastled_helpers.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 PaintYourDragon for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_fancyled.fastled_helpers`
 24  ====================================================
 25  
 26  CircuitPython "helper" library based on the Arduino FastLED library.
 27  Uses similar function names to assist with porting of existing Arduino FastLED
 28  projects to CircuitPython.
 29  
 30  * Author(s): PaintYourDragon
 31  """
 32  
 33  # imports
 34  
 35  __version__ = "0.0.0-auto.0"
 36  __repo__ = "https://github.com/Adafruit/Adafruit_CircuitPython_FancyLED.git"
 37  
 38  from math import floor
 39  from adafruit_fancyled import adafruit_fancyled as fancy
 40  
 41  # These are helper functions that provide more FastLED-like calls for
 42  # fancyled functions.
 43  # Function names are kept the same as FastLED, which normally upsets pylint.
 44  # Disable name-checking so this passes muster.
 45  # pylint: disable=invalid-name
 46  
 47  GFACTOR = 2.5  # Default gamma-correction factor for function below
 48  
 49  
 50  def applyGamma_video(n, g_r=GFACTOR, g_g=None, g_b=None, inplace=False):
 51      """ Approximates various invocations of FastLED's many-ways-overloaded
 52      applyGamma_video() function.
 53  
 54      ACCEPTS: One of three ways:
 55        1. A single brightness level (0-255) and optional gamma-correction
 56           factor (float usu. > 1.0, default if unspecified is 2.5).
 57        2. A single CRGB, CHSV or packed integer type and optional gamma
 58           factor or separate R, G, B gamma values.
 59        3. A list of CRGB, CHSV or packed integer types (and optional gamma(s)).
 60  
 61        In the tuple/list cases, the 'inplace' flag determines whether
 62        a new tuple/list is calculated and returned, or the existing
 63        value is modified in-place.  By default this is 'False'.
 64        Can also use the napplyGamma_video() function to more directly
 65        approximate FastLED syntax/behavior.
 66  
 67      RETURNS: Corresponding to above cases:
 68        1. Single gamma-corrected brightness level (0-255).
 69        2. A gamma-corrected CRGB value (even if input is CHSV or packed).
 70        3. A list of gamma-corrected CRGB values.
 71  
 72        In the tuple/list cases, there is NO return value if 'inplace'
 73        is true -- the original values are modified.
 74      """
 75  
 76      # If single gamma value is passed, keep that, otherwise convert
 77      # gamma values to tuple for gamma_adjust function.
 78      if g_g is not None and g_b is not None:
 79          g_r = (g_r, g_g, g_b)
 80  
 81      return fancy.gamma_adjust(n, g_r, inplace=inplace)
 82  
 83  
 84  def napplyGamma_video(n, g_r=GFACTOR, g_g=None, g_b=None):
 85      """ In-place version of applyGamma_video() (to mimic FastLED function
 86      name).  This is for RGB tuples and tuple lists (not the prior function's
 87      integer case)
 88      """
 89  
 90      return applyGamma_video(n, g_r, g_g, g_b, inplace=True)
 91  
 92  
 93  def loadDynamicGradientPalette(src, size):
 94      """ Kindasorta like FastLED's loadDynamicGradientPalette() function,
 95      with some gotchas.
 96  
 97      ACCEPTS: Gradient palette data as a 'bytes' type (makes it easier to copy
 98               over gradient palettes from existing FastLED Arduino sketches)...
 99               each palette entry is four bytes: a relative position (0-255)
100               within the overall resulting palette (whatever its size), and
101               3 values for R, G and B...and a length for a new palette list
102               to be allocated.
103  
104      RETURNS: list of CRGB colors.
105      """
106  
107      # Convert gradient from bytelist (groups of 4) to list of tuples,
108      # each consisting of a position (0.0 to 1.0) and CRGB color.
109      # (This is what FancyLED's expand_gradient needs for input.)
110      grad = []
111      for i in range(0, len(src), 4):
112          grad.append((src[i] / 255.0, fancy.CRGB(src[i + 1], src[i + 2], src[i + 3])))
113  
114      # Create palette (CRGB list) matching 'size' length
115      return fancy.expand_gradient(grad, size)
116  
117  
118  def ColorFromPalette(pal, pos, brightness=255, blend=False):
119      """ Approximates the FastLED ColorFromPalette() function
120  
121      ACCEPTS: color palette (list of CRGB, CSHV and/or packed ints),
122               palette index (x16) + blend factor of next index (0-15) --
123               e.g. pass 32 to retrieve palette index 2, or 40 for an
124               interpolated value between palette index 2 and 3, optional
125               brightness (0-255), optional blend flag (True/False)
126  
127      RETURNS: CRGB color, no gamma correction
128      """
129  
130      # Alter 'pos' from FastLED-like behavior to fancyled range
131      if blend:
132          # Continuous interpolation 0.0 to 1.0
133          pos = (pos / 16.0) / len(pal)
134      else:
135          # No blending -- quantize to nearest palette bin
136          pos = floor(pos / 16.0) / len(pal)
137  
138      color = fancy.palette_lookup(pal, pos)
139  
140      if brightness < 1.0:
141          brightness /= 255.0
142          if isinstance(color, fancy.CHSV):
143              color = fancy.CRGB(color)
144          elif isinstance(color, int):
145              color = fancy.unpack(color)
146          color.red *= brightness
147          color.green *= brightness
148          color.blue *= brightness
149  
150      return color
151  
152  
153  def hsv2rgb_spectrum(hue, sat, val):
154  
155      """ This is named the same thing as FastLED's simpler HSV to RGB function
156      (spectrum, vs rainbow) but implementation is a bit different for the
157      sake of getting something running (adapted from some NeoPixel code).
158  
159      ACCEPTS: hue, saturation, value in range 0 to 255
160      RETURNS: CRGB color.
161      """
162  
163      return fancy.CRGB(fancy.CHSV(hue / 255, sat / 255, val / 255))
164  
165  
166  # pylint: enable=invalid-name