103 lines
3.3 KiB
Python
103 lines
3.3 KiB
Python
# Standalone MicroPython Voice Animation: Working Gears
|
|
# Exposes run(text="", frames=...) and runs automatically if executed directly.
|
|
import time
|
|
import math
|
|
|
|
try:
|
|
import board_config
|
|
display = getattr(board_config, 'display_instance', None)
|
|
except Exception:
|
|
board_config = None
|
|
display = None
|
|
|
|
def draw_center_text(d, msg, y, w, scale=1, c=1):
|
|
char_w = 8 * scale
|
|
x = max(0, (w - len(msg) * char_w) // 2)
|
|
if hasattr(d, 'text_large') and scale > 1:
|
|
try:
|
|
d.text_large(msg, x, y, scale, c)
|
|
return
|
|
except Exception:
|
|
pass
|
|
d.text(msg, x, y, c)
|
|
|
|
def draw_gear(d, gcx, gcy, r, teeth_count, rotation, c=1):
|
|
# Draw gear boundary
|
|
d.rect(gcx - r, gcy - r, r * 2, r * 2, c)
|
|
# Inner shaft hole
|
|
d.fill_rect(gcx - 5, gcy - 5, 10, 10, c)
|
|
d.fill_rect(gcx - 2, gcy - 2, 4, 4, 0)
|
|
|
|
# Draw gear teeth extending outwards
|
|
for i in range(teeth_count):
|
|
angle = rotation + i * (2 * math.pi / teeth_count)
|
|
# Inner teeth start
|
|
x1 = gcx + int((r - 2) * math.cos(angle))
|
|
y1 = gcy + int((r - 2) * math.sin(angle))
|
|
# Outer teeth end
|
|
x2 = gcx + int((r + 10) * math.cos(angle))
|
|
y2 = gcy + int((r + 10) * math.sin(angle))
|
|
|
|
d.line(x1, y1, x2, y2, c)
|
|
|
|
# Add tiny horizontal heads to teeth for thickness
|
|
perp_angle = angle + math.pi / 2
|
|
tx1 = x2 - int(3 * math.cos(perp_angle))
|
|
ty1 = y2 - int(3 * math.sin(perp_angle))
|
|
tx2 = x2 + int(3 * math.cos(perp_angle))
|
|
ty2 = y2 + int(3 * math.sin(perp_angle))
|
|
d.line(tx1, ty1, tx2, ty2, c)
|
|
|
|
def run(text="", frames=100):
|
|
if not display:
|
|
print("board_config.display_instance is not available. Skipping animation.")
|
|
return
|
|
|
|
width = getattr(display, 'width', 400)
|
|
height = getattr(display, 'height', 300)
|
|
cx, cy = width // 2, height // 2
|
|
|
|
label = text if text else "WORKING..."
|
|
frame = 0
|
|
|
|
try:
|
|
while True:
|
|
if frames is not None and frames > 0 and frame >= frames:
|
|
break
|
|
|
|
display.clear(0)
|
|
|
|
# Draw two rotating, interlocking gears
|
|
# Large Gear (Left & slightly up)
|
|
rot1 = frame * 0.08
|
|
draw_gear(display, cx - 45, cy - 10, r=32, teeth_count=8, rotation=rot1, c=1)
|
|
|
|
# Small Gear (Right & slightly down)
|
|
# Rotates counter-clockwise, offset to interlock teeth
|
|
rot2 = -frame * 0.106 + (math.pi / 8)
|
|
draw_gear(display, cx + 45, cy + 20, r=24, teeth_count=6, rotation=rot2, c=1)
|
|
|
|
# Little decorative background computer dots
|
|
dot_phase = (frame // 4) % 4
|
|
for i in range(4):
|
|
fill_color = 1 if i == dot_phase else 0
|
|
display.rect(cx - 80 + i * 16, cy + 70, 8, 8, 1)
|
|
if fill_color:
|
|
display.fill_rect(cx - 78 + i * 16, cy + 72, 4, 4, 1)
|
|
|
|
# Draw centered text label at the bottom
|
|
draw_center_text(display, label, height - 30, width, scale=1, c=1)
|
|
|
|
display.show()
|
|
frame += 1
|
|
|
|
try:
|
|
time.sleep_ms(50)
|
|
except AttributeError:
|
|
time.sleep(0.05)
|
|
except KeyboardInterrupt:
|
|
print("Animation interrupted by user.")
|
|
|
|
if __name__ in ('__main__', '__name__'):
|
|
run()
|