1+ """Core easing annotations and helper functions."""
12from collections .abc import Callable
23from math import cos , pi , sin , sqrt , tau
34from typing import Protocol , TypeVar
45
56T = TypeVar ("T" )
67
78
9+ # This needs to be a Protocol rather than an annotation
10+ # due to our build configuration being set to pick up
11+ # classes but not type annotations.
12+ class EasingFunction (Protocol ):
13+ """Any :py:func:`callable` object which maps linear completion to a curve.
14+
15+ .. tip:: See :py:class:`Easing` for the most common easings.
16+
17+ Pass them to :py:func:`.ease` via the ``func``
18+ keyword argument.
19+
20+ If the built-in easing curves are not enough, you can define
21+ your own. Functions should match this pattern:
22+
23+ .. code-block:: python
24+
25+ def f(t: float) -> t:
26+ ...
27+
28+ For advanced users, any object with a matching :py:meth:`~object.__call__`
29+ method can be passed as an easing function.
30+ """
31+
32+ def __call__ (self , __t : float ) -> float :
33+ ...
34+
35+
36+
837class Animatable (Protocol ):
38+ """Matches types with support for the following operations:
39+
40+ .. list-table::
41+ :header-rows: 1
42+
43+ * - Method
44+ - Summary
45+
46+ * - :py:meth:`~object.__mul__`
47+ - Multiplication by a scalar
48+
49+ * - :py:meth:`~object.__add__`
50+ - Addition
51+
52+ * - :py:meth:`~object.__sub__`
53+ - Subtraction
54+
55+ .. important:: The :py:mod:`pyglet.math` matrix types are currently unsupported.
56+
57+ Although vector types work, matrix multiplication is
58+ subtly different. It uses a separate :py:meth:`~object.__matmul__`
59+ operator for multiplication.
60+ """
61+
62+
963 def __mul__ (self : T , other : T | float , / ) -> T : ...
1064
1165 def __add__ (self : T , other : T | float , / ) -> T : ...
@@ -30,7 +84,29 @@ def __sub__(self: T, other: T | float, /) -> T: ...
3084
3185
3286class Easing :
33- """:py:class:`.EasingFunction`s meant for passing into :py:meth:`.ease`."""
87+ """Built-in easing functions as static methods.
88+
89+ Each takes the following form:
90+
91+ .. code-block:: python
92+
93+ def f(t: float) -> float:
94+ ...
95+
96+ Pass them into :py:func:`.ease` via the ``func`` keyword
97+ argument:
98+
99+ .. code-block:: python
100+
101+ from arcade.anim import ease, Easing
102+
103+ value = ease(
104+ 1.0, 2.0,
105+ 2.0, 3.0,
106+ 2.4,
107+ func=Easing.SINE_IN)
108+
109+ """
34110
35111 # This is a bucket of staticmethods because typing.
36112 # Enum hates this, and they can't be classmethods.
@@ -259,23 +335,33 @@ def _clamp(x: float, low: float, high: float) -> float:
259335
260336
261337def perc (x : float , start : float , end : float ) -> float :
262- """
263- Convert a value ``x`` to be a percentage of progression between
264- ``start`` and ``end``.
338+ """Convert ``x`` to percent-like progress from ``start`` to ``end``.
339+
340+ Arguments:
341+ x: A value between ``start`` and ``end``.
342+ start: The start of the range.
343+ end: The end of the range.
344+
345+ Returns:
346+ A normalized percent-like completion as a :py:class:`float`.
265347 """
266348 return (x - start ) / (end - start )
267349
268350
269351def lerp (x : float , minimum : A , maximum : A ) -> A :
270- """
271- Convert a percentage ``x`` to be the value when progressed
272- that amount between ``minimum`` and ``maximum``.
352+ """Get ``x`` of the way from ``minimum`` to ``maximum``.
353+
354+ Arguments:
355+ x: A percent-like progress measure from ``0`` to ``1.0``.
356+ minimum: The start value along the path.
357+ maximum: The maximum value along the path.
358+
359+ Returns:
360+ A value ``x`` of the way from ``minimum`` to ``maximum``.
273361 """
274362 return minimum + ((maximum - minimum ) * x )
275363
276364
277- EasingFunction = Callable [[float ], float ]
278-
279365
280366def ease (
281367 minimum : A ,
@@ -286,21 +372,55 @@ def ease(
286372 func : EasingFunction = Easing .LINEAR ,
287373 clamped : bool = True ,
288374) -> A :
289- """Ease a value according to a curve. Useful for animating properties over time.
375+ """Ease a value according to a curve function passed as ``func``.
376+
377+ Override the default easing curve by passing any :py:class:`.Easing`
378+ or :py:class:`.EasingFunction` of your choice.
379+
380+ The ``maximum`` and ``minimum`` must be of compatible types.
381+ For example, these can include:
382+
383+ .. list-table::
384+ :header-rows: 1
385+
386+ * - Type
387+ - Value Example
388+ - Explanation
389+
390+ * - :py:class:`float`
391+ - ``0.5``
392+ - Numbers such as volume or brightness.
290393
291- Args:
394+ * - :py:class:`~pyglet.math.Vec2`
395+ - ``Vec2(500.0, 200.0)``
396+ - A :py:mod:`pyglet.math` vector representing position.
397+
398+ Arguments:
292399 minimum: any math-like object (a position, scale, value...); the "start position."
293400 maximum: any math-like object (a position, scale, value...); the "end position."
294401 start: a :py:class:`float` defining where progression begins, the "start time."
295402 end: a :py:class:`float` defining where progression ends, the "end time."
296403 t: a :py:class:`float` defining the current progression, the "current time."
297- func: a :py:class:`.EasingFunction` to modify the result with, typically an
298- attribute of :py:class:`.Easing`. Defaults to :py:attr:`.Easing.LINEAR`.
299- clamped: a :py:class:`bool`; whether or not to allow the animation to continue past
300- the ``start`` and ``end`` "times". Defaults to ``True``.
404+ func: Defaults to :py:attr:`Easing.LINEAR`, but you can pass an
405+ :py:class:`Easing` or :py:class:`.EasingFunction` of your choice.
406+ clamped: Whether the value will be clamped to ``minimum`` and ``maximum``.
407+
408+ Returns:
409+ An eased value for the given time ``t``.
410+
301411 """
302412 p = perc (t , start , end )
303413 if clamped :
304414 p = _clamp (p , 0.0 , 1.0 )
305415 new_p = func (p )
306416 return lerp (new_p , minimum , maximum )
417+
418+ __all__ = [
419+ "Animatable" ,
420+ "Easing" ,
421+ "EasingFunction" ,
422+ "ease" ,
423+ "perc" ,
424+ "lerp"
425+ ]
426+
0 commit comments