nextrpg.geometry.sizable

  1from functools import cached_property
  2from typing import TYPE_CHECKING, Protocol, runtime_checkable
  3
  4from nextrpg.geometry.anchor import Anchor
  5from nextrpg.geometry.anchored_coordinate import (
  6    BottomCenterCoordinate,
  7    BottomLeftCoordinate,
  8    BottomRightCoordinate,
  9    CenterCoordinate,
 10    CenterLeftCoodinate,
 11    CenterRightCoordinate,
 12    TopCenterCoordinate,
 13    TopRightCoordinate,
 14)
 15from nextrpg.geometry.coordinate import Coordinate, XAxis, YAxis
 16from nextrpg.geometry.size import Height, Size, Width
 17
 18if TYPE_CHECKING:
 19    from nextrpg.geometry.rectangle_area_on_screen import RectangleAreaOnScreen
 20
 21
 22@runtime_checkable
 23class Sizable(Protocol):
 24    # Subclass shall implement these.
 25    size: Size
 26    top_left: Coordinate
 27
 28    @cached_property
 29    def rectangle_area_on_screen(self) -> RectangleAreaOnScreen:
 30        from nextrpg.geometry.rectangle_area_on_screen import (
 31            RectangleAreaOnScreen,
 32        )
 33
 34        return RectangleAreaOnScreen(self.top_left, self.size)
 35
 36    @cached_property
 37    def width(self) -> Width:
 38        return self.size.width
 39
 40    @cached_property
 41    def height(self) -> Height:
 42        return self.size.height
 43
 44    @cached_property
 45    def top(self) -> YAxis:
 46        return self.top_left.top
 47
 48    @cached_property
 49    def left(self) -> XAxis:
 50        return self.top_left.left
 51
 52    @cached_property
 53    def bottom(self) -> YAxis:
 54        return self.top_left.top + self.size.height
 55
 56    @cached_property
 57    def right(self) -> XAxis:
 58        return self.top_left.left + self.size.width
 59
 60    @cached_property
 61    def top_right(self) -> TopRightCoordinate:
 62        left, top = self.top_left
 63        return TopRightCoordinate(left + self.width.value, top)
 64
 65    @cached_property
 66    def bottom_left(self) -> BottomLeftCoordinate:
 67        left, top = self.top_left
 68        return BottomLeftCoordinate(left, top + self.height.value)
 69
 70    @cached_property
 71    def bottom_right(self) -> BottomRightCoordinate:
 72        left, top = self.top_left
 73        return BottomRightCoordinate(
 74            left + self.width.value, top + self.height.value
 75        )
 76
 77    @cached_property
 78    def top_center(self) -> TopCenterCoordinate:
 79        left, top = self.top_left
 80        return TopCenterCoordinate(left + self.width.value / 2, top)
 81
 82    @cached_property
 83    def bottom_center(self) -> BottomCenterCoordinate:
 84        left, top = self.top_left
 85        return BottomCenterCoordinate(
 86            left + self.width.value / 2, top + self.height.value
 87        )
 88
 89    @cached_property
 90    def center_left(self) -> CenterLeftCoodinate:
 91        left, top = self.top_left
 92        return CenterLeftCoodinate(left, top + self.height.value / 2)
 93
 94    @cached_property
 95    def center_right(self) -> CenterRightCoordinate:
 96        left, top = self.top_left
 97        return CenterRightCoordinate(
 98            left + self.width.value, top + self.height.value / 2
 99        )
100
101    @cached_property
102    def center(self) -> CenterCoordinate:
103        left, top = self.top_left
104        return CenterCoordinate(
105            left + self.width.value / 2, top + self.height.value / 2
106        )
107
108    def at_anchor(self, anchor: Anchor) -> Coordinate:
109        match anchor:
110            case Anchor.TOP_LEFT:
111                return self.top_left
112            case Anchor.TOP_CENTER:
113                return self.top_center
114            case Anchor.TOP_RIGHT:
115                return self.top_right
116            case Anchor.CENTER_LEFT:
117                return self.center_left
118            case Anchor.CENTER:
119                return self.center
120            case Anchor.CENTER_RIGHT:
121                return self.center_right
122            case Anchor.BOTTOM_LEFT:
123                return self.bottom_left
124            case Anchor.BOTTOM_CENTER:
125                return self.bottom_center
126            case Anchor.BOTTOM_RIGHT:
127                return self.bottom_right
@runtime_checkable
class Sizable(typing.Protocol):
 23@runtime_checkable
 24class Sizable(Protocol):
 25    # Subclass shall implement these.
 26    size: Size
 27    top_left: Coordinate
 28
 29    @cached_property
 30    def rectangle_area_on_screen(self) -> RectangleAreaOnScreen:
 31        from nextrpg.geometry.rectangle_area_on_screen import (
 32            RectangleAreaOnScreen,
 33        )
 34
 35        return RectangleAreaOnScreen(self.top_left, self.size)
 36
 37    @cached_property
 38    def width(self) -> Width:
 39        return self.size.width
 40
 41    @cached_property
 42    def height(self) -> Height:
 43        return self.size.height
 44
 45    @cached_property
 46    def top(self) -> YAxis:
 47        return self.top_left.top
 48
 49    @cached_property
 50    def left(self) -> XAxis:
 51        return self.top_left.left
 52
 53    @cached_property
 54    def bottom(self) -> YAxis:
 55        return self.top_left.top + self.size.height
 56
 57    @cached_property
 58    def right(self) -> XAxis:
 59        return self.top_left.left + self.size.width
 60
 61    @cached_property
 62    def top_right(self) -> TopRightCoordinate:
 63        left, top = self.top_left
 64        return TopRightCoordinate(left + self.width.value, top)
 65
 66    @cached_property
 67    def bottom_left(self) -> BottomLeftCoordinate:
 68        left, top = self.top_left
 69        return BottomLeftCoordinate(left, top + self.height.value)
 70
 71    @cached_property
 72    def bottom_right(self) -> BottomRightCoordinate:
 73        left, top = self.top_left
 74        return BottomRightCoordinate(
 75            left + self.width.value, top + self.height.value
 76        )
 77
 78    @cached_property
 79    def top_center(self) -> TopCenterCoordinate:
 80        left, top = self.top_left
 81        return TopCenterCoordinate(left + self.width.value / 2, top)
 82
 83    @cached_property
 84    def bottom_center(self) -> BottomCenterCoordinate:
 85        left, top = self.top_left
 86        return BottomCenterCoordinate(
 87            left + self.width.value / 2, top + self.height.value
 88        )
 89
 90    @cached_property
 91    def center_left(self) -> CenterLeftCoodinate:
 92        left, top = self.top_left
 93        return CenterLeftCoodinate(left, top + self.height.value / 2)
 94
 95    @cached_property
 96    def center_right(self) -> CenterRightCoordinate:
 97        left, top = self.top_left
 98        return CenterRightCoordinate(
 99            left + self.width.value, top + self.height.value / 2
100        )
101
102    @cached_property
103    def center(self) -> CenterCoordinate:
104        left, top = self.top_left
105        return CenterCoordinate(
106            left + self.width.value / 2, top + self.height.value / 2
107        )
108
109    def at_anchor(self, anchor: Anchor) -> Coordinate:
110        match anchor:
111            case Anchor.TOP_LEFT:
112                return self.top_left
113            case Anchor.TOP_CENTER:
114                return self.top_center
115            case Anchor.TOP_RIGHT:
116                return self.top_right
117            case Anchor.CENTER_LEFT:
118                return self.center_left
119            case Anchor.CENTER:
120                return self.center
121            case Anchor.CENTER_RIGHT:
122                return self.center_right
123            case Anchor.BOTTOM_LEFT:
124                return self.bottom_left
125            case Anchor.BOTTOM_CENTER:
126                return self.bottom_center
127            case Anchor.BOTTOM_RIGHT:
128                return self.bottom_right

Base class for protocol classes.

Protocol classes are defined as::

class Proto(Protocol):
    def meth(self) -> int:
        ...

Such classes are primarily used with static type checkers that recognize structural subtyping (static duck-typing).

For example::

class C:
    def meth(self) -> int:
        return 0

def func(x: Proto) -> int:
    return x.meth()

func(C())  # Passes static type check

See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable act as simple-minded runtime protocols that check only the presence of given attributes, ignoring their type signatures. Protocol classes can be generic, they are defined as::

class GenProto[T](Protocol):
    def meth(self) -> T:
        ...
Sizable(*args, **kwargs)
1866def _no_init_or_replace_init(self, *args, **kwargs):
1867    cls = type(self)
1868
1869    if cls._is_protocol:
1870        raise TypeError('Protocols cannot be instantiated')
1871
1872    # Already using a custom `__init__`. No need to calculate correct
1873    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1874    if cls.__init__ is not _no_init_or_replace_init:
1875        return
1876
1877    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1878    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1879    # searches for a proper new `__init__` in the MRO. The new `__init__`
1880    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1881    # instantiation of the protocol subclass will thus use the new
1882    # `__init__` and no longer call `_no_init_or_replace_init`.
1883    for base in cls.__mro__:
1884        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1885        if init is not _no_init_or_replace_init:
1886            cls.__init__ = init
1887            break
1888    else:
1889        # should not happen
1890        cls.__init__ = object.__init__
1891
1892    cls.__init__(self, *args, **kwargs)
rectangle_area_on_screen
29    @cached_property
30    def rectangle_area_on_screen(self) -> RectangleAreaOnScreen:
31        from nextrpg.geometry.rectangle_area_on_screen import (
32            RectangleAreaOnScreen,
33        )
34
35        return RectangleAreaOnScreen(self.top_left, self.size)
width: nextrpg.geometry.size.Width
37    @cached_property
38    def width(self) -> Width:
39        return self.size.width
height: nextrpg.geometry.size.Height
41    @cached_property
42    def height(self) -> Height:
43        return self.size.height
45    @cached_property
46    def top(self) -> YAxis:
47        return self.top_left.top
49    @cached_property
50    def left(self) -> XAxis:
51        return self.top_left.left
bottom: nextrpg.geometry.coordinate.YAxis
53    @cached_property
54    def bottom(self) -> YAxis:
55        return self.top_left.top + self.size.height
57    @cached_property
58    def right(self) -> XAxis:
59        return self.top_left.left + self.size.width
61    @cached_property
62    def top_right(self) -> TopRightCoordinate:
63        left, top = self.top_left
64        return TopRightCoordinate(left + self.width.value, top)
66    @cached_property
67    def bottom_left(self) -> BottomLeftCoordinate:
68        left, top = self.top_left
69        return BottomLeftCoordinate(left, top + self.height.value)
71    @cached_property
72    def bottom_right(self) -> BottomRightCoordinate:
73        left, top = self.top_left
74        return BottomRightCoordinate(
75            left + self.width.value, top + self.height.value
76        )
78    @cached_property
79    def top_center(self) -> TopCenterCoordinate:
80        left, top = self.top_left
81        return TopCenterCoordinate(left + self.width.value / 2, top)
83    @cached_property
84    def bottom_center(self) -> BottomCenterCoordinate:
85        left, top = self.top_left
86        return BottomCenterCoordinate(
87            left + self.width.value / 2, top + self.height.value
88        )
90    @cached_property
91    def center_left(self) -> CenterLeftCoodinate:
92        left, top = self.top_left
93        return CenterLeftCoodinate(left, top + self.height.value / 2)
 95    @cached_property
 96    def center_right(self) -> CenterRightCoordinate:
 97        left, top = self.top_left
 98        return CenterRightCoordinate(
 99            left + self.width.value, top + self.height.value / 2
100        )
102    @cached_property
103    def center(self) -> CenterCoordinate:
104        left, top = self.top_left
105        return CenterCoordinate(
106            left + self.width.value / 2, top + self.height.value / 2
107        )
def at_anchor( self, anchor: nextrpg.geometry.anchor.Anchor) -> nextrpg.geometry.coordinate.Coordinate:
109    def at_anchor(self, anchor: Anchor) -> Coordinate:
110        match anchor:
111            case Anchor.TOP_LEFT:
112                return self.top_left
113            case Anchor.TOP_CENTER:
114                return self.top_center
115            case Anchor.TOP_RIGHT:
116                return self.top_right
117            case Anchor.CENTER_LEFT:
118                return self.center_left
119            case Anchor.CENTER:
120                return self.center
121            case Anchor.CENTER_RIGHT:
122                return self.center_right
123            case Anchor.BOTTOM_LEFT:
124                return self.bottom_left
125            case Anchor.BOTTOM_CENTER:
126                return self.bottom_center
127            case Anchor.BOTTOM_RIGHT:
128                return self.bottom_right