Guest User

Untitled

a guest
Mar 19th, 2021
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.64 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. import random
  3. import time
  4. import sys
  5. import argparse
  6. from collections import defaultdict
  7.  
  8. import board
  9. import busio
  10. import adafruit_ssd1306
  11. from PIL import Image, ImageDraw, ImageFont
  12.  
  13.  
  14. parser = argparse.ArgumentParser()
  15. parser.add_argument('spell', help='The emojimagick spell string, including '
  16.                      'the binding emoji on start and end.')
  17. parser.add_argument('--width', type=int, default=128,
  18.                      help='Width of the OLED (default: 128)')
  19. parser.add_argument('--height', type=int, default=32,
  20.                      help='Height of the OLED (default: 32)')
  21. parser.add_argument('--font', default='./Symbola.otf',
  22.                      help='Emoji font to use (default: ./Symbola.otf)')
  23. parser.add_argument('--font-size', type=int, default=19,
  24.                     help='The font size (default: 19)')
  25. parser.add_argument('--rounds', type=int, default=7,
  26.                     help='How many times to cycle the spell at each binding '
  27.                     '(default: 7)')
  28. parser.add_argument('--showtime', type=float, default=0.7,
  29.                      help='How long to show each emoji cast, in seconds '
  30.                      '(default: 0.7)')
  31. parser.add_argument('--binding_showtime', type=float, default=None,
  32.                     help='Timestep of binding animation (default: showtime/77)')
  33. args = parser.parse_args()
  34. if not args.binding_showtime:
  35.     args.binding_showtime = args.showtime/77
  36.  
  37.  
  38. class EmojiAutoCaster():
  39.     def __init__(self, spell, oled, font: ImageFont,
  40.                  rounds: int, showtime: float, binding_showtime: float,
  41.                 ):
  42.         (self.spell, self.oled, self.font,
  43.          self.rounds, self.showtime, self.binding_showtime) = (
  44.              spell, oled, font, rounds, showtime, binding_showtime)
  45.  
  46.         # the emojimagick binding characters
  47.         self.binding_start = self.spell[0]
  48.         self.binding_end   = self.spell[-1]
  49.  
  50.         # '1' = 1-bit color.
  51.         self.image = Image.new("1", (oled.width, oled.height))
  52.         self.draw = ImageDraw.Draw(self.image)
  53.  
  54.         # this dict is used to clear characters before redraw.
  55.         #
  56.         # each character has an arbitrary name, and the dictionary keeps track
  57.         # of the previous rectangle that contained that named character.
  58.         self.previous = defaultdict(lambda: None)
  59.  
  60.         self.oled.fill(0) # clear
  61.  
  62.  
  63.     def drawborder(self, margin:int=1, mirror:bool=False, fill:int=1):
  64.         left=0+margin
  65.         top=0+margin
  66.         bottom=self.oled.height-margin
  67.         right=self.oled.width-margin
  68.  
  69.         for x in range(left, right):
  70.             if mirror:
  71.                 self.draw.point((right-x, top),    fill=fill)
  72.                 self.draw.point((x,       bottom), fill=fill)
  73.             else:
  74.                 self.draw.point((x,       top),    fill=fill)
  75.                 self.draw.point((right-x, bottom), fill=fill)
  76.             self.oled.image(self.image)
  77.             self.oled.show()
  78.  
  79.  
  80.     def redrawborder(self, margin:int=1):
  81.         self.drawborder(mirror=True, fill=0)
  82.         self.drawborder(mirror=False, fill=1)
  83.  
  84.  
  85.     def cast_emoji_centred(self,
  86.                            emoji: str,
  87.                            charname: str,
  88.                            x: int,
  89.                            y: int=0,
  90.                            previous_rect: (int,int,int,int)=None) \
  91.             -> (int, int, int, int):
  92.         '''Draw centred on x,y.  Returns rect of drawing.
  93.  
  94.        charname is an arbitrary identifier; if a character with this
  95.        identifier was drawn before, its rectangle will be erased first.'''
  96.  
  97.         if self.previous[charname]:
  98.             self.draw.rectangle(self.previous[charname], fill=0)
  99.  
  100.         (emoji_width, emoji_height) = self.font.getsize(emoji)
  101.  
  102.         x = x - emoji_width / 2
  103.         y = y - emoji_height / 2
  104.  
  105.         self.draw.text(
  106.             (x, y),
  107.             emoji,
  108.             font=self.font,
  109.             fill=1,
  110.         )
  111.  
  112.         previous_rect = (x, y, x+emoji_width, y+emoji_height)
  113.         self.previous[charname] = previous_rect
  114.  
  115.  
  116.     def initial_bind(self):
  117.         y = self.oled.height/2
  118.         x = 0
  119.         while x <= self.oled.width/2:
  120.             self.cast_emoji_centred(
  121.                 self.binding_start,
  122.                 'binding start',
  123.                 x, y)
  124.  
  125.             self.cast_emoji_centred(
  126.                 self.binding_end,
  127.                 'binding end',
  128.                 self.oled.width-x, y)
  129.  
  130.             self.oled.image(self.image)
  131.  
  132.             self.oled.show()
  133.  
  134.             time.sleep(self.binding_showtime)
  135.  
  136.             x += 1
  137.  
  138.     def cast(self):
  139.         working=self.spell[1:-1]
  140.         i=0
  141.         self.drawborder()
  142.         self.initial_bind()
  143.  
  144.         # loop over one emoji at a time
  145.         while True:
  146.             if i == self.rounds * (len(working)):
  147.                 self.redrawborder()
  148.                 self.initial_bind()
  149.                 i=0
  150.             working_element=working[i%len(working)]
  151.             # working_element=working_element[0] # modifiers not well rendered
  152.  
  153.             # always redraw the binding (invisibly) first
  154.             (binding_start_width, binding_start_height) = self.font.getsize(self.binding_start)
  155.             self.cast_emoji_centred(self.binding_start, 'binding start',
  156.                                     self.oled.width/2-binding_start_width-7,
  157.                                     self.oled.height/2)
  158.             self.oled.image(self.image)
  159.  
  160.             self.cast_emoji_centred(working_element, 'working element',
  161.                                    self.oled.width/2, self.oled.height/2,)
  162.             self.oled.image(self.image)
  163.  
  164.             (binding_end_width, binding_end_height) = self.font.getsize(self.binding_end)
  165.             self.cast_emoji_centred(self.binding_start, 'binding end',
  166.                                     self.oled.width/2+binding_end_width+7,
  167.                                     self.oled.height/2,)
  168.             self.oled.image(self.image)
  169.  
  170.             self.oled.show()
  171.  
  172.             time.sleep(self.showtime)
  173.             i+=1
  174.  
  175.  
  176. def getCaster():
  177.     i2c = busio.I2C(board.SCL, board.SDA)
  178.     oled = adafruit_ssd1306.SSD1306_I2C(args.width, args.height, i2c)
  179.     try:
  180.         emojifont = ImageFont.truetype(args.font, args.font_size)
  181.     except OSError as e:
  182.         sys.stderr.write(f"Could not open font {args.font}:\n")
  183.         sys.stderr.write(str(e))
  184.         sys.stderr.write("\n")
  185.         sys.exit(1)
  186.  
  187.     return EmojiAutoCaster(args.spell, oled, emojifont,
  188.                              args.rounds, args.showtime, args.binding_showtime,
  189.                             )
  190.  
  191. getCaster().cast()
  192.  
Advertisement
Add Comment
Please, Sign In to add comment