code.py
1 # SPDX-FileCopyrightText: 2019 Carter Nelson for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 from random import randrange 6 import board 7 import busio 8 import displayio 9 from adafruit_gizmo import tft_gizmo 10 import adafruit_imageload 11 import adafruit_lis3dh 12 13 #---| User Config |--------------- 14 BACKGROUND = "/blinka_dark.bmp" # specify color or background BMP file 15 16 NUM_FLAKES = 50 # total number of snowflakes 17 FLAKE_SHEET = "/flakes_sheet.bmp" # flake sprite sheet 18 FLAKE_WIDTH = 4 # sprite width 19 FLAKE_HEIGHT = 4 # sprite height 20 FLAKE_TRAN_COLOR = 0x000000 # transparency color 21 22 SNOW_COLOR = 0xFFFFFF # snow color 23 24 SHAKE_THRESHOLD = 27 # shake sensitivity, lower=more sensitive 25 #---| User Config |--------------- 26 27 # Accelerometer setup 28 accelo_i2c = busio.I2C(board.ACCELEROMETER_SCL, board.ACCELEROMETER_SDA) 29 accelo = adafruit_lis3dh.LIS3DH_I2C(accelo_i2c, address=0x19) 30 31 # Create the TFT Gizmo display 32 display = tft_gizmo.TFT_Gizmo() 33 34 # Load background image 35 try: 36 bg_bitmap, bg_palette = adafruit_imageload.load(BACKGROUND, 37 bitmap=displayio.Bitmap, 38 palette=displayio.Palette) 39 # Or just use solid color 40 except (OSError, TypeError, AttributeError): 41 BACKGROUND = BACKGROUND if isinstance(BACKGROUND, int) else 0x000000 42 bg_bitmap = displayio.Bitmap(display.width, display.height, 1) 43 bg_palette = displayio.Palette(1) 44 bg_palette[0] = BACKGROUND 45 background = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette) 46 47 48 # Snowflake setup 49 flake_bitmap, flake_palette = adafruit_imageload.load(FLAKE_SHEET, 50 bitmap=displayio.Bitmap, 51 palette=displayio.Palette) 52 if FLAKE_TRAN_COLOR is not None: 53 for i, color in enumerate(flake_palette): 54 if color == FLAKE_TRAN_COLOR: 55 flake_palette.make_transparent(i) 56 break 57 NUM_SPRITES = flake_bitmap.width // FLAKE_WIDTH * flake_bitmap.height // FLAKE_HEIGHT 58 flake_pos = [0.0] * NUM_FLAKES 59 flakes = displayio.Group() 60 for _ in range(NUM_FLAKES): 61 flakes.append(displayio.TileGrid(flake_bitmap, pixel_shader=flake_palette, 62 width = 1, 63 height = 1, 64 tile_width = FLAKE_WIDTH, 65 tile_height = FLAKE_HEIGHT, 66 x = randrange(0, display.width), 67 default_tile=randrange(0, NUM_SPRITES))) 68 69 # Snowfield setup 70 snow_depth = [display.height] * display.width 71 snow_palette = displayio.Palette(2) 72 snow_palette[0] = 0xADAF00 # transparent color 73 snow_palette[1] = SNOW_COLOR # snow color 74 snow_palette.make_transparent(0) 75 snow_bitmap = displayio.Bitmap(display.width, display.height, len(snow_palette)) 76 snow = displayio.TileGrid(snow_bitmap, pixel_shader=snow_palette) 77 78 # Add everything to display 79 splash = displayio.Group() 80 splash.append(background) 81 splash.append(flakes) 82 splash.append(snow) 83 display.show(splash) 84 85 def clear_the_snow(): 86 #pylint: disable=global-statement, redefined-outer-name 87 global flakes, flake_pos, snow_depth 88 display.auto_refresh = False 89 for flake in flakes: 90 # set to a random sprite 91 flake[0] = randrange(0, NUM_SPRITES) 92 # set to a random x location 93 flake.x = randrange(0, display.width) 94 # set random y locations, off screen to start 95 flake_pos = [-1.0*randrange(0, display.height) for _ in range(NUM_FLAKES)] 96 # reset snow level 97 snow_depth = [display.height] * display.width 98 # and snow bitmap 99 for i in range(display.width*display.height): 100 snow_bitmap[i] = 0 101 display.auto_refresh = True 102 103 def add_snow(index, amount, steepness=2): 104 location = [] 105 # local steepness check 106 for x in range(index - amount, index + amount): 107 add = False 108 if x == 0: 109 # check depth to right 110 if snow_depth[x+1] - snow_depth[x] < steepness: 111 add = True 112 elif x == display.width - 1: 113 # check depth to left 114 if snow_depth[x-1] - snow_depth[x] < steepness: 115 add = True 116 elif 0 < x < display.width - 1: 117 # check depth to left AND right 118 if snow_depth[x-1] - snow_depth[x] < steepness and \ 119 snow_depth[x+1] - snow_depth[x] < steepness: 120 add = True 121 if add: 122 location.append(x) 123 # add where snow is not too steep 124 for x in location: 125 new_level = snow_depth[x] - 1 126 if new_level >= 0: 127 snow_depth[x] = new_level 128 snow_bitmap[x, new_level] = 1 129 130 while True: 131 clear_the_snow() 132 # loop until globe is full of snow 133 while snow_depth.count(0) < display.width: 134 # check for shake 135 if accelo.shake(SHAKE_THRESHOLD, 5, 0): 136 break 137 # update snowflakes 138 for i, flake in enumerate(flakes): 139 # speed based on sprite index 140 flake_pos[i] += 1 - flake[0] / NUM_SPRITES 141 # check if snowflake has hit the ground 142 if flake_pos[i] >= snow_depth[flake.x]: 143 # add snow where it fell 144 add_snow(flake.x, FLAKE_WIDTH) 145 # reset flake to top 146 flake_pos[i] = 0 147 # at a new x location 148 flake.x = randrange(0, display.width) 149 flake.y = int(flake_pos[i]) 150 display.refresh()