Skip to content

Commit

Permalink
typehints
Browse files Browse the repository at this point in the history
  • Loading branch information
JaskRendix committed Jan 23, 2024
1 parent b1ee25c commit 7c451b9
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 62 deletions.
36 changes: 22 additions & 14 deletions pyscroll/animation.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
from __future__ import annotations

from collections import namedtuple
from collections.abc import Sequence
from typing import Union
from typing import NamedTuple, Union

from pygame import Surface


class AnimationFrame(NamedTuple):
image: Surface
duration: float


AnimationFrame = namedtuple("AnimationFrame", "image duration")
TimeLike = Union[float, int]

__all__ = ("AnimationFrame", "AnimationToken")


class AnimationToken:
__slots__ = ["next", "positions", "frames", "index"]

def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None:
__slots__ = ["_next", "positions", "frames", "index"]

def __init__(
self,
positions: set[tuple[int, int, int]],
frames: Sequence[AnimationFrame],
initial_time: float = 0.0,
) -> None:
"""
Constructor
Expand All @@ -26,10 +37,10 @@ def __init__(self, positions, frames: Sequence, initial_time: int = 0) -> None:
frames = tuple(AnimationFrame(*i) for i in frames)
self.positions = positions
self.frames = frames
self.next = frames[0].duration + initial_time
self._next = frames[0].duration + initial_time
self.index = 0

def advance(self, last_time: TimeLike):
def advance(self, last_time: TimeLike) -> AnimationFrame:
"""
Advance the frame, and set timer for next frame
Expand All @@ -52,11 +63,8 @@ def advance(self, last_time: TimeLike):

# set the timer for the next advance
next_frame = self.frames[self.index]
self.next = next_frame.duration + last_time
self._next = next_frame.duration + last_time
return next_frame

def __lt__(self, other):
try:
return self.next < other.next
except AttributeError:
return self.next < other
def __lt__(self, other: AnimationToken) -> bool:
return self._next < other._next
48 changes: 28 additions & 20 deletions pyscroll/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
from collections.abc import Iterable
from heapq import heappop, heappush
from itertools import product
from typing import Any

import pygame
from pygame import Surface
from pygame import Rect, Surface

try:
# optional pytmx support
Expand Down Expand Up @@ -48,20 +49,25 @@ class PyscrollDataAdapter:
tile_size: tuple[int, int] = (0, 0)
# (int, int): size of map in tiles
map_size: tuple[int, int] = (0, 0)
visible_tile_layers = None # list of visible layer integers
# list of visible layer integers
visible_tile_layers: list[int] = []

def __init__(self) -> None:
self._last_time = None # last time map animations were updated
self._animation_queue = list() # list of animation tokens
self._animated_tile = dict() # mapping of tile substitutions when animated
self._tracked_tiles = set() # track the tiles on screen with animations
# last time map animations were updated
self._last_time = 0.0
# list of animation tokens
self._animation_queue: list[AnimationToken] = []
# mapping of tile substitutions when animated
self._animated_tile: dict[tuple[int, int, int], Surface] = {}
# track the tiles on screen with animations
self._tracked_tiles = set()

def reload_data(self) -> None:
raise NotImplementedError

def process_animation_queue(
self,
tile_view: RectLike,
tile_view: Rect,
) -> list[tuple[int, int, int, Surface]]:
"""
Given the time and the tile view, process tile changes and return them
Expand All @@ -75,7 +81,7 @@ def process_animation_queue(
# verify that there are tile substitutions ready
self._update_time()
try:
if self._animation_queue[0].next > self._last_time:
if self._animation_queue[0]._next > self._last_time:
return new_tiles

# raised with the animation queue is empty (no animations at all)
Expand All @@ -87,7 +93,7 @@ def process_animation_queue(
get_tile_image = self.get_tile_image

# test if the next scheduled tile change is ready
while self._animation_queue[0].next <= self._last_time:
while self._animation_queue[0]._next <= self._last_time:

# get the next tile/frame which is ready to be changed
token = heappop(self._animation_queue)
Expand Down Expand Up @@ -134,7 +140,7 @@ def _update_time(self) -> None:
"""
self._last_time = time.time() * 1000

def prepare_tiles(self, tiles: RectLike):
def prepare_tiles(self, tiles: RectLike) -> None:
"""
Somewhat experimental: The renderer will advise data layer of its view
Expand All @@ -158,14 +164,13 @@ def reload_animations(self) -> None:
"""
self._update_time()
self._animation_queue = list()
self._tracked_gids = set()
self._animation_map = dict()
self._tracked_gids: set[int] = set()
self._animation_map: dict[int, AnimationToken] = {}

for gid, frame_data in self.get_animations():
self._tracked_gids.add(gid)

frames = list()
frames: list[AnimationFrame] = []
for frame_gid, frame_duration in frame_data:
image = self._get_tile_image_by_id(frame_gid)
frames.append(AnimationFrame(image, frame_duration))
Expand All @@ -177,7 +182,7 @@ def reload_animations(self) -> None:
# locations of an animation, but searching for their locations
# is slow. so it will be updated as the map is drawn.

positions = set()
positions: set[tuple[int, int, int]] = set()
ani = AnimationToken(positions, frames, self._last_time)
self._animation_map[gid] = ani
heappush(self._animation_queue, ani)
Expand Down Expand Up @@ -224,7 +229,7 @@ def _get_tile_image(self, x: int, y: int, l: int) -> Surface:
"""
raise NotImplementedError

def _get_tile_image_by_id(self, id):
def _get_tile_image_by_id(self, id: int) -> Any:
"""
Return Image by a custom ID.
Expand All @@ -248,6 +253,9 @@ def get_animations(self) -> None:
Where Frames is:
[ (ID, Duration), ... ]
[tuple[int, tuple[int, float]]]
[tuple[gid, tuple[frame_gid, frame_duration]]]
And ID is a reference to a tile image.
This will be something accessible using _get_tile_image_by_id
Expand Down Expand Up @@ -348,14 +356,14 @@ def _get_tile_image(self, x: int, y: int, l: int):
except ValueError:
return None

def _get_tile_image_by_id(self, id) -> Surface:
def _get_tile_image_by_id(self, id: int) -> Surface:
return self.tmx.images[id]

def get_tile_images_by_rect(self, rect: RectLike):
def rev(seq:list[int], start:int, stop: int) -> enumerate[int]:
def rev(seq: list[int], start: int, stop: int) -> enumerate[int]:
if start < 0:
start = 0
return enumerate(seq[start:stop + 1], start)
return enumerate(seq[start : stop + 1], start)

x1, y1, x2, y2 = rect_to_bb(rect)
images = self.tmx.images
Expand Down Expand Up @@ -411,7 +419,7 @@ def _get_tile_image(self, x: int, y: int, l: int) -> Surface:
"""
pass

def _get_tile_image_by_id(self, id) -> None:
def _get_tile_image_by_id(self, id: int) -> None:
"""
Required for sprite collation - not implemented
Expand Down
61 changes: 33 additions & 28 deletions pyscroll/orthographic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import time
from collections.abc import Callable
from itertools import chain, product
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Optional

import pygame
from pygame import Rect, Surface
Expand Down Expand Up @@ -87,34 +87,39 @@ def __init__(
self._clear_color = None

# private attributes
self._anchored_view = True # if true, map is fixed to upper left corner
self._previous_blit = (
None # rect of the previous map blit when map edges are visible
)
self._size = None # actual pixel size of the view, as it occupies the screen
self._redraw_cutoff = (
None # size of dirty tile edge that will trigger full redraw
)
self._x_offset = None # offsets are used to scroll map in sub-tile increments
self._y_offset = None
self._buffer = None # complete rendering of tilemap
self._tile_view = None # this rect represents each tile on the buffer
self._half_width = None # 'half x' attributes are used to reduce division ops.
self._half_height = None
self._tile_queue = None # tiles queued to be draw onto buffer
self._animation_queue = (
None # heap queue of animation token; schedules tile changes
)
self._layer_quadtree = None # used to draw tiles that overlap optional surfaces
self._zoom_buffer = None # used to speed up zoom operations
# if true, map is fixed to upper left corner
self._anchored_view = True
# rect of the previous map blit when map edges are visible
self._previous_blit = None
# actual pixel size of the view, as it occupies the screen
self._size: tuple[int, int] = (0, 0)
# size of dirty tile edge that will trigger full redraw
self._redraw_cutoff = 0
# offsets are used to scroll map in sub-tile increments
self._x_offset = 0
self._y_offset = 0
# complete rendering of tilemap
self._buffer: Optional[Surface] = None
# this rect represents each tile on the buffer
self._tile_view = Rect(0, 0, 0, 0)
# 'half x' attributes are used to reduce division ops.
self._half_width = 0
self._half_height = 0
# tiles queued to be draw onto buffer
self._tile_queue = None
# heap queue of animation token; schedules tile changes
self._animation_queue = None
# used to draw tiles that overlap optional surfaces
self._layer_quadtree: Optional[FastQuadTree] = None
# used to speed up zoom operations
self._zoom_buffer = None
self._zoom_level = zoom
self._real_ratio_x = (
1.0 # zooming slightly changes aspect ratio; this compensates
)
self._real_ratio_y = (
1.0 # zooming slightly changes aspect ratio; this compensates
)
self.view_rect = Rect(0, 0, 0, 0) # this represents the viewable map pixels
# zooming slightly changes aspect ratio; this compensates
self._real_ratio_x = 1.0
# zooming slightly changes aspect ratio; this compensates
self._real_ratio_y = 1.0
# this represents the viewable map pixels
self.view_rect = Rect(0, 0, 0, 0)

self.set_size(size)

Expand Down

0 comments on commit 7c451b9

Please sign in to comment.