Skip to content

Commit

Permalink
Initial cleanup of arcade.types (#2065)
Browse files Browse the repository at this point in the history
* Turn arcade.types into folder package
* Move color types/aliases to arcade.types.color
* Remove unused RGBAOrA255OrNormalized
* Clean up arcade.types imports
* Remove unused TypeVar import
* Move BufferProtocol conditional import to the top
* Make backport-related imports conditional instead of universal
* Remove redundant NamedPoint named tuple
* Simplify V_2D and V_3D in arcade.math
* Remove Vec2 and Vec3 from the unions since pyglet's Vec tpyes are now tuples
* Remove Vec2 and Vec3 imports from arcade.math
* Remove Vector alias with 2 uses
* Remove currently broken and useless ColorLike type
* Broaden typing in V_2D, V_3D, and lerp
* Add AsFloat to arcade.types.__all__
* Clean up Path-related annotations
* Add subscriptable PathOr alias
* Remove Optional from PathOrTexture
* Wrap usages of PathOrTexture in Optional
* Add bytes to PathLike Union alias
* Clean up imports in some PathOrTexture-using files
* Move the if TYPE_CHECKING import of Texture closer to the usage
* Clean up whitespace in arcade/types/__init__.py
* Add top-level docstring for arcade.types.color
* Add top-level docstring for arcade.types
* Use AsFloat in current stubbed Rect annotations
* Remove unused import
* Move __all__ to top of arcade.types.color
* Try to fix imports / __all__
* Defer total Vector replacement until later PR
* Add back Vector alias
* Rename it to Velocity in arcade.types
* Change imports in particles + annotations in particles to account for this
* Add Point3 to arcade.types.__all__
* Use consistent Optional type for LifeTimePartic.__init__'s filename_or_texture
* Skip linting import order in arcade.types
* noqa linters wrongly marking a type checking import as unused
* Add omitted color tuple aliases to __all__
* Mark color masking constants with Final[int]
* Fix noqa prefix for conditional Texture import
* Let someone else alphabetize the arcade.types.__all__ declaration; I'm just getting it to build.
* Document Size2D[int] and Size2D[float]
* Add types to the API doc build
* Add Size2D to types.__all__
* Replace IPoint with Size2D in arcade.texture.tools
  • Loading branch information
pushfoo authored Apr 23, 2024
1 parent d625a01 commit efd215b
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 144 deletions.
7 changes: 4 additions & 3 deletions arcade/examples/particle_fireworks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
python -m arcade.examples.particle_fireworks
"""
import random
from typing import Optional

import pyglet
from pyglet.math import Vec2

import arcade
from arcade.types import Point
from arcade.types import Point, PathOrTexture
from arcade.math import rand_in_rect, clamp, lerp, rand_in_circle, rand_on_circle
from arcade.types import PathOrTexture
from arcade.particles import (
Emitter,
LifetimeParticle,
Expand Down Expand Up @@ -127,7 +128,7 @@ class AnimatedAlphaParticle(LifetimeParticle):

def __init__(
self,
filename_or_texture: PathOrTexture,
filename_or_texture: Optional[PathOrTexture],
change_xy: Vec2,
start_alpha: int = 0,
duration1: float = 1.0,
Expand Down
5 changes: 2 additions & 3 deletions arcade/experimental/shapes_perf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import random

import arcade
from arcade.types import NamedPoint
from pyglet.math import Mat4

SCREEN_WIDTH = 800
Expand Down Expand Up @@ -93,12 +92,12 @@ def __init__(self, width, height, title):

# line strip
self.line_strip = [
NamedPoint(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
for _ in range(10)
]
# Random list of points
self.points = [
NamedPoint(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
for _ in range(10_000)
]

Expand Down
20 changes: 10 additions & 10 deletions arcade/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import math
import random
from typing import Tuple, List, Union
from pyglet.math import Vec2, Vec3
from arcade.types import Point, Vector
from typing import Sequence, Tuple, Union
from arcade.types import AsFloat, Point


_PRECISION = 2

Expand Down Expand Up @@ -63,13 +63,13 @@ def clamp(a, low: float, high: float) -> float:
return high if a > high else max(a, low)


def lerp(v1: float, v2: float, u: float) -> float:
"""linearly interpolate between two values"""
return v1 + ((v2 - v1) * u)
V_2D = Union[Tuple[AsFloat, AsFloat], Sequence[AsFloat]]
V_3D = Union[Tuple[AsFloat, AsFloat, AsFloat], Sequence[AsFloat]]


V_2D = Union[Vec2, Tuple[float, float], List[float]]
V_3D = Union[Vec3, Tuple[float, float, float], List[float]]
def lerp(v1: AsFloat, v2: AsFloat, u: float) -> float:
"""linearly interpolate between two values"""
return v1 + ((v2 - v1) * u)


def lerp_2d(v1: V_2D, v2: V_2D, u: float) -> Tuple[float, float]:
Expand Down Expand Up @@ -202,7 +202,7 @@ def rand_vec_spread_deg(
angle: float,
half_angle_spread: float,
length: float
) -> Vector:
) -> tuple[float, float]:
"""
Returns a random vector, within a spread of the given angle.
Expand All @@ -220,7 +220,7 @@ def rand_vec_magnitude(
angle: float,
lo_magnitude: float,
hi_magnitude: float,
) -> Vector:
) -> tuple[float, float]:
"""
Returns a random vector, within a spread of the given angle.
Expand Down
4 changes: 2 additions & 2 deletions arcade/particles/emitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from .particle import Particle
from typing import Optional, Callable, cast
from arcade.math import _Vec2
from arcade.types import Point, Vector
from arcade.types import Point, Velocity


class EmitController:
Expand Down Expand Up @@ -110,7 +110,7 @@ def __init__(
center_xy: Point,
emit_controller: EmitController,
particle_factory: Callable[["Emitter"], Particle],
change_xy: Vector = (0.0, 0.0),
change_xy: Velocity = (0.0, 0.0),
emit_done_cb: Optional[Callable[["Emitter"], None]] = None,
reap_cb: Optional[Callable[[], None]] = None
):
Expand Down
17 changes: 8 additions & 9 deletions arcade/particles/particle.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@
Particle - Object produced by an Emitter. Often used in large quantity to produce visual effects effects
"""
from __future__ import annotations
from typing import Literal
from typing import Literal, Optional, Tuple

from arcade.sprite import Sprite
from arcade.math import lerp, clamp
from arcade.types import Point, Vector
from arcade.types import PathOrTexture
from arcade.types import Point, PathOrTexture, Velocity


class Particle(Sprite):
"""Sprite that is emitted from an Emitter"""

def __init__(
self,
path_or_texture: PathOrTexture,
change_xy: Vector,
path_or_texture: Optional[PathOrTexture],
change_xy: Tuple[float, float],
center_xy: Point = (0.0, 0.0),
angle: float = 0.0,
change_angle: float = 0.0,
Expand Down Expand Up @@ -54,7 +53,7 @@ class EternalParticle(Particle):
def __init__(
self,
filename_or_texture: PathOrTexture,
change_xy: Vector,
change_xy: Velocity,
center_xy: Point = (0.0, 0.0),
angle: float = 0,
change_angle: float = 0,
Expand All @@ -75,8 +74,8 @@ class LifetimeParticle(Particle):

def __init__(
self,
filename_or_texture: PathOrTexture,
change_xy: Vector,
filename_or_texture: Optional[PathOrTexture],
change_xy: Velocity,
lifetime: float,
center_xy: Point = (0.0, 0.0),
angle: float = 0,
Expand Down Expand Up @@ -106,7 +105,7 @@ class FadeParticle(LifetimeParticle):
def __init__(
self,
filename_or_texture: PathOrTexture,
change_xy: Vector,
change_xy: Velocity,
lifetime: float,
center_xy: Point = (0.0, 0.0),
angle: float = 0,
Expand Down
2 changes: 1 addition & 1 deletion arcade/sprite/sprite.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class Sprite(BasicSprite, PymunkMixin):

def __init__(
self,
path_or_texture: PathOrTexture = None,
path_or_texture: Optional[PathOrTexture] = None,
scale: float = 1.0,
center_x: float = 0.0,
center_y: float = 0.0,
Expand Down
6 changes: 3 additions & 3 deletions arcade/texture/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import arcade
import arcade.cache
from .texture import ImageData, Texture
from arcade.types import IPoint
from arcade.types import Size2D
from arcade import cache

_DEFAULT_TEXTURE = None
Expand All @@ -20,7 +20,7 @@ def cleanup_texture_cache():
arcade.cache.image_data_cache.clear()


def get_default_texture(size: IPoint = _DEFAULT_IMAGE_SIZE) -> Texture:
def get_default_texture(size: Size2D[int] = _DEFAULT_IMAGE_SIZE) -> Texture:
"""
Creates and returns a default texture and caches it internally for future use.
Expand All @@ -38,7 +38,7 @@ def get_default_texture(size: IPoint = _DEFAULT_IMAGE_SIZE) -> Texture:
return _DEFAULT_TEXTURE


def get_default_image(size: IPoint = _DEFAULT_IMAGE_SIZE) -> ImageData:
def get_default_image(size: Size2D[int] = _DEFAULT_IMAGE_SIZE) -> ImageData:
"""
Generates and returns a default image and caches it internally for future use.
Expand Down
169 changes: 169 additions & 0 deletions arcade/types/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
"""Fundamental aliases, classes, and related constants.
As general rules:
#. Things only go in this module if they serve multiple purposes
throughout arcade
#. Only expose the most important classes at this module's top level
For example, color-related types and related aliases go in
``arcade.types`` because they're used throughout the codebase. This
includes all the following areas:
#. :py:class:`~arcade.Sprite`
#. :py:class:`~arcade.SpriteList`
#. :py:class:`~arcade.Text`
#. The :py:mod:`arcade.gui` widgets
#. Functions in :py:mod:`arcade.drawing_commands`
However, since the color types, aliases, and constants are all related,
they go in the :py:mod:`arcade.types.color` submodule.
"""
from __future__ import annotations

# Don't lint import order since we have conditional compatibility shims
# flake8: noqa: E402
import sys
from pathlib import Path
from typing import (
List,
NamedTuple,
Optional,
Sequence,
Tuple,
Union,
TYPE_CHECKING,
TypeVar
)

from pytiled_parser import Properties


# Backward-compatibility for buffer protocol objects
# To learn more, see https://docs.python.org/3.8/c-api/buffer.html
if sys.version_info >= (3, 12):
from collections.abc import Buffer as BufferProtocol
else:
# The most common built-in buffer protocol objects
import ctypes
from array import array
from collections.abc import ByteString

# This is used instead of the typing_extensions version since they
# use an ABC which registers virtual subclasses. This will not work
# with ctypes.Array since virtual subclasses must be concrete.
# See: https://peps.python.org/pep-0688/
BufferProtocol = Union[ByteString, memoryview, array, ctypes.Array]


#: 1. Makes pyright happier while also telling readers
#: 2. Tells readers we're converting any ints to floats
AsFloat = Union[float, int]


# Generic color aliases
from arcade.types.color import RGB
from arcade.types.color import RGBA
from arcade.types.color import RGBOrA

# Specific color aliases
from arcade.types.color import RGB255
from arcade.types.color import RGBA255
from arcade.types.color import RGBNormalized
from arcade.types.color import RGBANormalized
from arcade.types.color import RGBOrA255
from arcade.types.color import RGBOrANormalized

# The Color helper type
from arcade.types.color import Color


__all__ = [
"AsFloat",
"BufferProtocol",
"Color",
"IPoint",
"PathOr",
"PathOrTexture",
"Point",
"Point3",
"PointList",
"EMPTY_POINT_LIST",
"Rect",
"RectList",
"RGB",
"RGBA",
"RGBOrA",
"RGB255",
"RGBA255",
"RGBOrA255",
"RGBNormalized",
"RGBANormalized",
"RGBOrANormalized",
"Size2D",
"TiledObject",
"Velocity"
]


_T = TypeVar('_T')

#: ``Size2D`` helps mark int or float sizes. Use it like a
#: :py:class:`typing.Generic`'s bracket notation as follows:
#:
#: .. code-block:: python
#:
#: def example_Function(size: Size2D[int], color: RGBA255) -> Texture:
#: """An example of how to use Size2D.
#:
#: Look at signature above, not the missing function body. The
#: ``size`` argument is how you can mark texture sizes, while
#: you can use ``Size2D[float]`` to denote float regions.
#:
#: :param size: A made-up hypothetical argument.
#: :param color: Hypothetical texture-related argument.
#: """
#: ... # No function definition
#:
Size2D = Tuple[_T, _T]

# Point = Union[Tuple[AsFloat, AsFloat], List[AsFloat]]
Point = Tuple[AsFloat, AsFloat]
Point3 = Tuple[AsFloat, AsFloat, AsFloat]
IPoint = Tuple[int, int]


# We won't keep this forever. It's a temp stub for particles we'll replace.
Velocity = Tuple[AsFloat, AsFloat]

PointList = Sequence[Point]
# Speed / typing workaround:
# 1. Eliminate extra allocations
# 2. Allows type annotation to be cleaner, primarily for HitBox & subclasses
EMPTY_POINT_LIST: PointList = tuple()


Rect = Union[Tuple[int, int, int, int], List[int]] # x, y, width, height
RectList = Union[Tuple[Rect, ...], List[Rect]]
FloatRect = Union[Tuple[AsFloat, AsFloat, AsFloat, AsFloat], List[AsFloat]] # x, y, width, height


# Path handling
PathLike = Union[str, Path, bytes]
_POr = TypeVar('_POr') # Allows PathOr[TypeNameHere] syntax
PathOr = Union[PathLike, _POr]


# Specific utility resource aliases with type imports
if TYPE_CHECKING:
# The linters are wrong: this is used, so we noqa it
from arcade.texture import Texture # noqa: F401

PathOrTexture = PathOr["Texture"]


class TiledObject(NamedTuple):
shape: Union[Point, PointList, Rect]
properties: Optional[Properties] = None
name: Optional[str] = None
type: Optional[str] = None
Loading

0 comments on commit efd215b

Please sign in to comment.