def zoom_out_animation(self, touch_point): """Performs un-zoom animation when icon is released. :param touch_point: x,y location of the screen. :type touch_point: Tuple[x,y] :return: None """ if (self._animation_time > 0) and self._zoomed: _image, _palette = adafruit_imageload.load(self._icon) animation_bitmap = self.__class__.bitmap_buffer animation_palette = self.__class__.palette_buffer # store the current display refresh setting refresh_status = self.__class__.display.auto_refresh self.__class__.display.auto_refresh = False # set auto_refresh off # Animation: shrink down to the original size start_time = time.monotonic() while True: elapsed_time = time.monotonic() - start_time position = max( 0.0, easeout(1 - (elapsed_time / self._animation_time))) animation_bitmap.fill(len(animation_palette) - 1) bitmaptools.rotozoom( dest_bitmap=animation_bitmap, ox=animation_bitmap.width // 2, oy=animation_bitmap.height // 2, source_bitmap=_image, px=_image.width // 2, py=_image.height // 2, scale=1.0 + position * (self._scale - 1.0), angle=position * self._angle, ) self.__class__.display.refresh() if elapsed_time > self._animation_time: break # clean up the zoom display elements self[0].hidden = False # unhide the original icon self.pop(-1) # remove zoom tilegrid from the group self.__class__.display.refresh() # set display.auto_refresh back to original value self.__class__.display.auto_refresh = refresh_status del _image del _palette gc.collect() self._zoomed = False
def draw_labels( target_bitmap, *, font, font_height, tick_labels, dial_center, dial_radius, start_angle, sweep_angle, rotate_labels=True, tick_label_scale=1.0, ): """Helper function for drawing text labels on the dial widget. Can be used to customize the dial face. :param displayio.Bitmap target_bitmap: Bitmap where ticks will be drawn into :param Font font: the font to be used to draw the tick mark text labels :param int font_height: the height of the font, used for text placement :param List[str] tick_labels: a list of strings for the tick text labels :param (int,int) dial_center: the (x,y) pixel location in the bitmap of the dial's center of rotation :param int dial_radius: the radius of the dial (not including padding), in pixels :param int tick_count: number of ticks to be drawn :param int tick_stroke: the pixel width of the line used to draw the tick :param float start_angle: starting angle of the dial, in degrees :param float sweep_angle: total sweep angle of the dial, in degrees :param bool rotate_labels: set to True if you want the label text to be rotated to align with the tick marks :param float tick_label_scale: scale factor for the tick text labels, default is 1.0 """ label_count = len(tick_labels) for i, this_label_text in enumerate(tick_labels): temp_label = bitmap_label.Label( font, text=this_label_text ) # make a tick line bitmap for blitting this_angle = (2 * math.pi / 360) * ( start_angle + i * sweep_angle / (label_count - 1) ) # in radians target_position_x = dial_center[0] + ( dial_radius + font_height // 2 ) * math.sin(this_angle) target_position_y = dial_center[1] - ( dial_radius + font_height // 2 ) * math.cos(this_angle) if rotate_labels: pass else: this_angle = 0 if "rotozoom" in dir(bitmaptools): # if core function is available bitmaptools.rotozoom( target_bitmap, ox=round(target_position_x), oy=round(target_position_y), source_bitmap=temp_label.bitmap, px=round(temp_label.bitmap.width // 2), py=round(temp_label.bitmap.height // 2), angle=this_angle, scale=tick_label_scale, ) else: _blit_rotate_scale( # translate and rotate the tick into the target_bitmap destination=target_bitmap, ox=round(target_position_x), oy=round(target_position_y), source=temp_label.bitmap, px=round(temp_label.bitmap.width // 2), py=round(temp_label.bitmap.height // 2), angle=this_angle, scale=tick_label_scale, )
def draw_ticks( target_bitmap, *, dial_center, dial_radius, tick_count, tick_stroke, tick_length, start_angle, sweep_angle, tick_color_index=2, ): """Helper function for drawing ticks on the dial widget. Can be used to customize the dial face. :param displayio.Bitmap target_bitmap: Bitmap where ticks will be drawn into :param (int,int) dial_center: the (x,y) pixel location in the bitmap of the dial's center of rotation :param int dial_radius: the radius of the dial (not including padding), in pixels :param int tick_count: number of ticks to be drawn :param int tick_stroke: the pixel width of the line used to draw the tick :param float start_angle: starting angle of the dial, in degrees :param float sweep_angle: total sweep angle of the dial, in degrees :param int tick_color_index: the bitmap's color index that should be used for drawing the tick marks """ if tick_count <= 1: pass else: tick_bitmap = displayio.Bitmap( tick_stroke, tick_length, tick_color_index + 1 ) # make a tick line bitmap for blitting tick_bitmap.fill( tick_color_index ) # initialize the tick bitmap with the tick_color_index for i in range(tick_count): this_angle = round( (start_angle + ((i * sweep_angle / (tick_count - 1)))) * (2 * math.pi / 360), 4, ) # in radians target_position_x = dial_center[0] + dial_radius * math.sin(this_angle) target_position_y = dial_center[1] - dial_radius * math.cos(this_angle) if "rotozoom" in dir(bitmaptools): # if core function is available bitmaptools.rotozoom( target_bitmap, ox=round(target_position_x), oy=round(target_position_y), source_bitmap=tick_bitmap, px=round(tick_bitmap.width / 2), py=0, angle=this_angle, # in radians ) else: _blit_rotate_scale( # translate and rotate the tick into the target_bitmap destination=target_bitmap, ox=target_position_x, oy=target_position_y, source=tick_bitmap, px=int(tick_bitmap.width / 2), py=0, angle=this_angle, # in radians )
def zoom_animation(self, touch_point): """Performs zoom animation when icon is pressed. :param touch_point: x,y location of the screen. :type touch_point: Tuple[x,y] :return: None """ if self._animation_time > 0: try: _image, _palette = adafruit_imageload.load(self._icon) if len(self.__class__.palette_buffer) < len(_palette) + 1: self._animation_time = 0 # skip any animation print( "Warning: IconAnimated - icon bitmap exceeds IconAnimated.max_color_depth;" " defaulting to no animation") except NotImplementedError: self._animation_time = 0 # skip any animation print( "Warning: IconAnimated - True color BMP unsupported for animation;" " defaulting to no animation") if self._animation_time > 0: animation_bitmap = self.__class__.bitmap_buffer animation_palette = self.__class__.palette_buffer # store the current display refresh setting refresh_status = self.__class__.display.auto_refresh ### ## Update the zoom palette and bitmap buffers and append the tilegrid ### # copy the image palette, add a transparent color at the end for i, color in enumerate(_palette): animation_palette[i] = color animation_palette[len(animation_palette) - 1] = 0x000000 animation_palette.make_transparent(len(animation_palette) - 1) # create the zoom bitmap larger than the original image to allow for zooming animation_bitmap.fill(len(animation_palette) - 1) # transparent fill animation_bitmap.blit( (animation_bitmap.width - _image.width) // 2, (animation_bitmap.height - _image.height) // 2, _image, ) # blit the image into the center of the zoom_bitmap # place zoom_bitmap at same location as image animation_tilegrid = TileGrid(animation_bitmap, pixel_shader=animation_palette) animation_tilegrid.x = -(animation_bitmap.width - _image.width) // 2 animation_tilegrid.y = -(animation_bitmap.height - _image.height) // 2 self.__class__.display.auto_refresh = False # set auto_refresh off self[0].hidden = True # hide the original icon self.append(animation_tilegrid) # add to the self group. # Animation: zoom larger start_time = time.monotonic() while True: elapsed_time = time.monotonic() - start_time position = min( 1.0, easein(elapsed_time / self._animation_time)) # fractional position animation_bitmap.fill(len(animation_palette) - 1) bitmaptools.rotozoom( dest_bitmap=animation_bitmap, ox=animation_bitmap.width // 2, oy=animation_bitmap.height // 2, source_bitmap=_image, px=_image.width // 2, py=_image.height // 2, scale=1.0 + position * (self._scale - 1.0), # start scaling at 1.0 angle=position * self._angle, ) self.__class__.display.refresh() if elapsed_time > self._animation_time: break # set display.auto_refresh back to original value self.__class__.display.auto_refresh = refresh_status del _image del _palette gc.collect() self._zoomed = True
percent = 0.0 last_time = time.monotonic() while True: percent = map_range(analog_in.value, (200, 65400), (1, 0)) theta = percent_to_theta(percent) print("dt:", time.monotonic() - last_time, "theta:", theta, int(percent * 100)) last_time = time.monotonic() # erasing the entire bitmap is slow (~1fps, because of transparency I think) # instead we erase just the region we modified, after refresh below # bitmap_scribble.fill(0) # offset rotation point (15,105) for bitmap_pointer's axis of rotation bitmaptools.rotozoom(bitmap_scribble, bitmap_pointer, angle=theta, px=15, py=105) display.refresh() # after refresh, now "erase" the rotated pointer by doing a # rotozom of a "blank" bitmap with only transparency bitmaptools.rotozoom(bitmap_scribble, bitmap_pointer_blank, angle=theta, px=15, py=105)