Skip to content

Commit 145caad

Browse files
authored
Merge pull request #912 from caternuson/snowglobe
Add code for TFT Gizmo Snowglobe
2 parents 9d15de0 + 5765765 commit 145caad

File tree

2 files changed

+306
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)