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
Sizable23@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)
top_left: nextrpg.geometry.coordinate.Coordinate
width: nextrpg.geometry.size.Width
height: nextrpg.geometry.size.Height
bottom_center: nextrpg.geometry.anchored_coordinate.BottomCenterCoordinate
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