Module textual.geometry
Functions and classes to manage terminal geometry (anything involving coordinates or dimensions).
Expand source code
"""
Functions and classes to manage terminal geometry (anything involving coordinates or dimensions).
"""
from __future__ import annotations
import sys
from functools import lru_cache
from operator import attrgetter, itemgetter
from typing import Any, Collection, NamedTuple, Tuple, TypeVar, Union, cast
if sys.version_info >= (3, 10):
from typing import TypeAlias
else: # pragma: no cover
from typing_extensions import TypeAlias
SpacingDimensions: TypeAlias = Union[
int, Tuple[int], Tuple[int, int], Tuple[int, int, int, int]
]
T = TypeVar("T", int, float)
def clamp(value: T, minimum: T, maximum: T) -> T:
"""Adjust a value to it is not less than a minimum and not greater
than a maximum value.
Args:
value (T): A value.
minimum (T): Minimum value.
maximum (T): maximum value.
Returns:
T: New value that is not less than the minimum or greater than the maximum.
"""
if minimum > maximum:
maximum, minimum = minimum, maximum
if value < minimum:
return minimum
elif value > maximum:
return maximum
else:
return value
class Offset(NamedTuple):
"""A cell offset defined by x and y coordinates. Offsets are typically relative to the
top left of the terminal or other container.
Textual prefers the names `x` and `y`, but you could consider `x` to be the _column_ and `y` to be the _row_.
"""
x: int = 0
"""Offset in the x-axis (horizontal)"""
y: int = 0
"""Offset in the y-axis (vertical)"""
@property
def is_origin(self) -> bool:
"""Check if the point is at the origin (0, 0).
Returns:
bool: True if the offset is the origin.
"""
return self == (0, 0)
@property
def clamped(self) -> Offset:
"""Ensure x and y are above zero.
Returns:
Offset: New offset.
"""
x, y = self
return Offset(0 if x < 0 else x, 0 if y < 0 else y)
def __bool__(self) -> bool:
return self != (0, 0)
def __add__(self, other: object) -> Offset:
if isinstance(other, tuple):
_x, _y = self
x, y = other
return Offset(_x + x, _y + y)
return NotImplemented
def __sub__(self, other: object) -> Offset:
if isinstance(other, tuple):
_x, _y = self
x, y = other
return Offset(_x - x, _y - y)
return NotImplemented
def __mul__(self, other: object) -> Offset:
if isinstance(other, (float, int)):
x, y = self
return Offset(int(x * other), int(y * other))
return NotImplemented
def __neg__(self) -> Offset:
x, y = self
return Offset(-x, -y)
def blend(self, destination: Offset, factor: float) -> Offset:
"""Blend (interpolate) to a new point.
Args:
destination (Point): Point where factor would be 1.0.
factor (float): A value between 0 and 1.0.
Returns:
Point: A new point on a line between self and destination.
"""
x1, y1 = self
x2, y2 = destination
return Offset(
int(x1 + (x2 - x1) * factor),
int(y1 + (y2 - y1) * factor),
)
def get_distance_to(self, other: Offset) -> float:
"""Get the distance to another offset.
Args:
other (Offset): An offset.
Returns:
float: Distance to other offset.
"""
x1, y1 = self
x2, y2 = other
distance = ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ** 0.5
return distance
class Size(NamedTuple):
"""The dimensions of a rectangular region."""
width: int = 0
"""The width in cells."""
height: int = 0
"""The height in cells."""
def __bool__(self) -> bool:
"""A Size is Falsy if it has area 0."""
return self.width * self.height != 0
@property
def area(self) -> int:
"""Get the area of the size.
Returns:
int: Area in cells.
"""
return self.width * self.height
@property
def region(self) -> Region:
"""Get a region of the same size.
Returns:
Region: A region with the same size at (0, 0).
"""
width, height = self
return Region(0, 0, width, height)
@property
def line_range(self) -> range:
"""Get a range covering lines.
Returns:
range: A builtin range object.
"""
return range(self.height)
def __add__(self, other: object) -> Size:
if isinstance(other, tuple):
width, height = self
width2, height2 = other
return Size(max(0, width + width2), max(0, height + height2))
return NotImplemented
def __sub__(self, other: object) -> Size:
if isinstance(other, tuple):
width, height = self
width2, height2 = other
return Size(max(0, width - width2), max(0, height - height2))
return NotImplemented
def contains(self, x: int, y: int) -> bool:
"""Check if a point is in area defined by the size.
Args:
x (int): X coordinate.
y (int): Y coordinate.
Returns:
bool: True if the point is within the region.
"""
width, height = self
return width > x >= 0 and height > y >= 0
def contains_point(self, point: tuple[int, int]) -> bool:
"""Check if a point is in the area defined by the size.
Args:
point (tuple[int, int]): A tuple of x and y coordinates.
Returns:
bool: True if the point is within the region.
"""
x, y = point
width, height = self
return width > x >= 0 and height > y >= 0
def __contains__(self, other: Any) -> bool:
try:
x, y = other
except Exception:
raise TypeError(
"Dimensions.__contains__ requires an iterable of two integers"
)
width, height = self
return width > x >= 0 and height > y >= 0
class Region(NamedTuple):
"""Defines a rectangular region.
A Region consists of a coordinate (x and y) and dimensions (width and height).
```
(x, y)
┌────────────────────┐ ▲
│ │ │
│ │ │
│ │ height
│ │ │
│ │ │
└────────────────────┘ ▼
◀─────── width ──────▶
```
"""
x: int = 0
"""Offset in the x-axis (horizontal)."""
y: int = 0
"""Offset in the y-axis (vertical)."""
width: int = 0
"""The width of the region."""
height: int = 0
"""The height of the region."""
@classmethod
def from_union(cls, regions: Collection[Region]) -> Region:
"""Create a Region from the union of other regions.
Args:
regions (Collection[Region]): One or more regions.
Returns:
Region: A Region that encloses all other regions.
"""
if not regions:
raise ValueError("At least one region expected")
min_x = min(regions, key=itemgetter(0)).x
max_x = max(regions, key=attrgetter("right")).right
min_y = min(regions, key=itemgetter(1)).y
max_y = max(regions, key=attrgetter("bottom")).bottom
return cls(min_x, min_y, max_x - min_x, max_y - min_y)
@classmethod
def from_corners(cls, x1: int, y1: int, x2: int, y2: int) -> Region:
"""Construct a Region form the top left and bottom right corners.
Args:
x1 (int): Top left x.
y1 (int): Top left y.
x2 (int): Bottom right x.
y2 (int): Bottom right y.
Returns:
Region: A new region.
"""
return cls(x1, y1, x2 - x1, y2 - y1)
@classmethod
def from_offset(cls, offset: tuple[int, int], size: tuple[int, int]) -> Region:
"""Create a region from offset and size.
Args:
offset (Point): Offset (top left point).
size (tuple[int, int]): Dimensions of region.
Returns:
Region: A region instance.
"""
x, y = offset
width, height = size
return cls(x, y, width, height)
@classmethod
def get_scroll_to_visible(
cls, window_region: Region, region: Region, *, top: bool = False
) -> Offset:
"""Calculate the smallest offset required to translate a window so that it contains
another region.
This method is used to calculate the required offset to scroll something in to view.
Args:
window_region (Region): The window region.
region (Region): The region to move inside the window.
top (bool, optional): Get offset to top of window. Defaults to False
Returns:
Offset: An offset required to add to region to move it inside window_region.
"""
if region in window_region:
# Region is already inside the window, so no need to move it.
return Offset(0, 0)
window_left, window_top, window_right, window_bottom = window_region.corners
region = region.crop_size(window_region.size)
left, top_, right, bottom = region.corners
delta_x = delta_y = 0
if not (
(window_right > left >= window_left)
and (window_right > right >= window_left)
):
# The region does not fit
# The window needs to scroll on the X axis to bring region in to view
delta_x = min(
left - window_left,
left - (window_right - region.width),
key=abs,
)
if not (
(window_bottom > top_ >= window_top)
and (window_bottom > bottom >= window_top)
):
# The window needs to scroll on the Y axis to bring region in to view
if top:
delta_y = top_ - window_top
else:
delta_y = min(
top_ - window_top,
top_ - (window_bottom - region.height),
key=abs,
)
return Offset(delta_x, delta_y)
def __bool__(self) -> bool:
"""A Region is considered False when it has no area."""
_, _, width, height = self
return width * height > 0
@property
def column_span(self) -> tuple[int, int]:
"""Get the start and end columns (x coord).
The end value is exclusive.
Returns:
tuple[int, int]: Pair of x coordinates (column numbers).
"""
return (self.x, self.x + self.width)
@property
def line_span(self) -> tuple[int, int]:
"""Get the start and end line number (y coord).
The end value is exclusive.
Returns:
tuple[int, int]: Pair of y coordinates (line numbers).
"""
return (self.y, self.y + self.height)
@property
def right(self) -> int:
"""Maximum X value (non inclusive).
Returns:
int: x coordinate.
"""
return self.x + self.width
@property
def bottom(self) -> int:
"""Maximum Y value (non inclusive).
Returns:
int: y coordinate.
"""
return self.y + self.height
@property
def area(self) -> int:
"""Get the area within the region.
Returns:
int: Area covered by this region.
"""
return self.width * self.height
@property
def offset(self) -> Offset:
"""Get the start point of the region.
Returns:
Offset: Top left offset.
"""
return Offset(self.x, self.y)
@property
def bottom_left(self) -> Offset:
"""Bottom left offset of the region.
Returns:
Offset: Bottom left offset.
"""
x, y, _width, height = self
return Offset(x, y + height)
@property
def top_right(self) -> Offset:
"""Top right offset of the region.
Returns:
Offset: Top right.
"""
x, y, width, _height = self
return Offset(x + width, y)
@property
def bottom_right(self) -> Offset:
"""Bottom right of the region.
Returns:
Offset: Bottom right.
"""
x, y, width, height = self
return Offset(x + width, y + height)
@property
def size(self) -> Size:
"""Get the size of the region.
Returns:
Size: Size of the region.
"""
return Size(self.width, self.height)
@property
def corners(self) -> tuple[int, int, int, int]:
"""Get the top left and bottom right coordinates as a tuple of integers.
Returns:
tuple[int, int, int, int]: A tuple of `(<left>, <top>, <right>, <bottom>)`.
"""
x, y, width, height = self
return x, y, x + width, y + height
@property
def column_range(self) -> range:
"""A range object for X coordinates."""
return range(self.x, self.x + self.width)
@property
def line_range(self) -> range:
"""A range object for Y coordinates."""
return range(self.y, self.y + self.height)
@property
def reset_offset(self) -> Region:
"""An region of the same size at (0, 0).
Returns:
Region: reset region.
"""
_, _, width, height = self
return Region(0, 0, width, height)
def __add__(self, other: object) -> Region:
if isinstance(other, tuple):
ox, oy = other
x, y, width, height = self
return Region(x + ox, y + oy, width, height)
return NotImplemented
def __sub__(self, other: object) -> Region:
if isinstance(other, tuple):
ox, oy = other
x, y, width, height = self
return Region(x - ox, y - oy, width, height)
return NotImplemented
def at_offset(self, offset: tuple[int, int]) -> Region:
"""Get a new Region with the same size at a given offset.
Args:
offset (tuple[int, int]): An offset.
Returns:
Region: New Region with adjusted offset.
"""
x, y = offset
_x, _y, width, height = self
return Region(x, y, width, height)
def crop_size(self, size: tuple[int, int]) -> Region:
"""Get a region with the same offset, with a size no larger than `size`.
Args:
size (tuple[int, int]): Maximum width and height (WIDTH, HEIGHT).
Returns:
Region: New region that could fit within `size`.
"""
x, y, width1, height1 = self
width2, height2 = size
return Region(x, y, min(width1, width2), min(height1, height2))
def expand(self, size: tuple[int, int]) -> Region:
"""Increase the size of the region by adding a border.
Args:
size (tuple[int, int]): Additional width and height.
Returns:
Region: A new region.
"""
expand_width, expand_height = size
x, y, width, height = self
return Region(
x - expand_width,
y - expand_height,
width + expand_width * 2,
height + expand_height * 2,
)
def clip_size(self, size: tuple[int, int]) -> Region:
"""Clip the size to fit within minimum values.
Args:
size (tuple[int, int]): Maximum width and height.
Returns:
Region: No region, not bigger than size.
"""
x, y, width, height = self
max_width, max_height = size
return Region(x, y, min(width, max_width), min(height, max_height))
@lru_cache(maxsize=1024)
def overlaps(self, other: Region) -> bool:
"""Check if another region overlaps this region.
Args:
other (Region): A Region.
Returns:
bool: True if other region shares any cells with this region.
"""
x, y, x2, y2 = self.corners
ox, oy, ox2, oy2 = other.corners
return ((x2 > ox >= x) or (x2 > ox2 > x) or (ox < x and ox2 >= x2)) and (
(y2 > oy >= y) or (y2 > oy2 > y) or (oy < y and oy2 >= y2)
)
def contains(self, x: int, y: int) -> bool:
"""Check if a point is in the region.
Args:
x (int): X coordinate.
y (int): Y coordinate.
Returns:
bool: True if the point is within the region.
"""
self_x, self_y, width, height = self
return (self_x + width > x >= self_x) and (self_y + height > y >= self_y)
def contains_point(self, point: tuple[int, int]) -> bool:
"""Check if a point is in the region.
Args:
point (tuple[int, int]): A tuple of x and y coordinates.
Returns:
bool: True if the point is within the region.
"""
x1, y1, x2, y2 = self.corners
try:
ox, oy = point
except Exception:
raise TypeError(f"a tuple of two integers is required, not {point!r}")
return (x2 > ox >= x1) and (y2 > oy >= y1)
@lru_cache(maxsize=1024)
def contains_region(self, other: Region) -> bool:
"""Check if a region is entirely contained within this region.
Args:
other (Region): A region.
Returns:
bool: True if the other region fits perfectly within this region.
"""
x1, y1, x2, y2 = self.corners
ox, oy, ox2, oy2 = other.corners
return (
(x2 >= ox >= x1)
and (y2 >= oy >= y1)
and (x2 >= ox2 >= x1)
and (y2 >= oy2 >= y1)
)
def translate(self, offset: tuple[int, int]) -> Region:
"""Move the offset of the Region.
Args:
offset (tuple[int, int]): Offset to add to region.
Returns:
Region: A new region shifted by (x, y)
"""
self_x, self_y, width, height = self
offset_x, offset_y = offset
return Region(self_x + offset_x, self_y + offset_y, width, height)
@lru_cache(maxsize=4096)
def __contains__(self, other: Any) -> bool:
"""Check if a point is in this region."""
if isinstance(other, Region):
return self.contains_region(other)
else:
try:
return self.contains_point(other)
except TypeError:
return False
def clip(self, width: int, height: int) -> Region:
"""Clip this region to fit within width, height.
Args:
width (int): Width of bounds.
height (int): Height of bounds.
Returns:
Region: Clipped region.
"""
x1, y1, x2, y2 = self.corners
_clamp = clamp
new_region = Region.from_corners(
_clamp(x1, 0, width),
_clamp(y1, 0, height),
_clamp(x2, 0, width),
_clamp(y2, 0, height),
)
return new_region
def grow(self, margin: tuple[int, int, int, int]) -> Region:
"""Grow a region by adding spacing.
Args:
margin (tuple[int, int, in, int]): Grow space by `(<top>, <right>, <bottom>, <left>)`.
Returns:
Region: New region.
"""
top, right, bottom, left = margin
x, y, width, height = self
return Region(
x=x - left,
y=y - top,
width=max(0, width + left + right),
height=max(0, height + top + bottom),
)
def shrink(self, margin: tuple[int, int, int, int]) -> Region:
"""Shrink a region by subtracting spacing.
Args:
margin (tuple[int, int, int, int]): Shrink space by `(<top>, <right>, <bottom>, <left>)`.
Returns:
Region: The new, smaller region.
"""
top, right, bottom, left = margin
x, y, width, height = self
return Region(
x=x + left,
y=y + top,
width=max(0, width - (left + right)),
height=max(0, height - (top + bottom)),
)
@lru_cache(maxsize=4096)
def intersection(self, region: Region) -> Region:
"""Get the overlapping portion of the two regions.
Args:
region (Region): A region that overlaps this region.
Returns:
Region: A new region that covers when the two regions overlap.
"""
# Unrolled because this method is used a lot
x1, y1, w1, h1 = self
cx1, cy1, w2, h2 = region
x2 = x1 + w1
y2 = y1 + h1
cx2 = cx1 + w2
cy2 = cy1 + h2
rx1 = cx2 if x1 > cx2 else (cx1 if x1 < cx1 else x1)
ry1 = cy2 if y1 > cy2 else (cy1 if y1 < cy1 else y1)
rx2 = cx2 if x2 > cx2 else (cx1 if x2 < cx1 else x2)
ry2 = cy2 if y2 > cy2 else (cy1 if y2 < cy1 else y2)
return Region(rx1, ry1, rx2 - rx1, ry2 - ry1)
@lru_cache(maxsize=4096)
def union(self, region: Region) -> Region:
"""Get the smallest region that contains both regions.
Args:
region (Region): Another region.
Returns:
Region: An optimally sized region to cover both regions.
"""
x1, y1, x2, y2 = self.corners
ox1, oy1, ox2, oy2 = region.corners
union_region = self.from_corners(
min(x1, ox1), min(y1, oy1), max(x2, ox2), max(y2, oy2)
)
return union_region
@lru_cache(maxsize=1024)
def split(self, cut_x: int, cut_y: int) -> tuple[Region, Region, Region, Region]:
"""Split a region in to 4 from given x and y offsets (cuts).
```
cut_x ↓
┌────────┐ ┌───┐
│ │ │ │
│ 0 │ │ 1 │
│ │ │ │
cut_y → └────────┘ └───┘
┌────────┐ ┌───┐
│ 2 │ │ 3 │
└────────┘ └───┘
```
Args:
cut_x (int): Offset from self.x where the cut should be made. If negative, the cut
is taken from the right edge.
cut_y (int): Offset from self.y where the cut should be made. If negative, the cut
is taken from the lower edge.
Returns:
tuple[Region, Region, Region, Region]: Four new regions which add up to the original (self).
"""
x, y, width, height = self
if cut_x < 0:
cut_x = width + cut_x
if cut_y < 0:
cut_y = height + cut_y
_Region = Region
return (
_Region(x, y, cut_x, cut_y),
_Region(x + cut_x, y, width - cut_x, cut_y),
_Region(x, y + cut_y, cut_x, height - cut_y),
_Region(x + cut_x, y + cut_y, width - cut_x, height - cut_y),
)
@lru_cache(maxsize=1024)
def split_vertical(self, cut: int) -> tuple[Region, Region]:
"""Split a region in to two, from a given x offset.
```
cut ↓
┌────────┐┌───┐
│ 0 ││ 1 │
│ ││ │
└────────┘└───┘
```
Args:
cut (int): An offset from self.x where the cut should be made. If cut is negative,
it is taken from the right edge.
Returns:
tuple[Region, Region]: Two regions, which add up to the original (self).
"""
x, y, width, height = self
if cut < 0:
cut = width + cut
return (
Region(x, y, cut, height),
Region(x + cut, y, width - cut, height),
)
@lru_cache(maxsize=1024)
def split_horizontal(self, cut: int) -> tuple[Region, Region]:
"""Split a region in to two, from a given x offset.
```
┌─────────┐
│ 0 │
│ │
cut → └─────────┘
┌─────────┐
│ 1 │
└─────────┘
```
Args:
cut (int): An offset from self.x where the cut should be made. May be negative,
for the offset to start from the right edge.
Returns:
tuple[Region, Region]: Two regions, which add up to the original (self).
"""
x, y, width, height = self
if cut < 0:
cut = height + cut
return (
Region(x, y, width, cut),
Region(x, y + cut, width, height - cut),
)
class Spacing(NamedTuple):
"""The spacing around a renderable."""
top: int = 0
"""Space from the top of a region."""
right: int = 0
"""Space from the left of a region."""
bottom: int = 0
"""Space from the bottom of a region."""
left: int = 0
"""Space from the left of a region."""
def __bool__(self) -> bool:
return self != (0, 0, 0, 0)
@property
def width(self) -> int:
"""Total space in width.
Returns:
int: Width.
"""
return self.left + self.right
@property
def height(self) -> int:
"""Total space in height.
Returns:
int: Height.
"""
return self.top + self.bottom
@property
def top_left(self) -> tuple[int, int]:
"""Top left space.
Returns:
tuple[int, int]: `(<left>, <top>)`
"""
return (self.left, self.top)
@property
def bottom_right(self) -> tuple[int, int]:
"""Bottom right space.
Returns:
tuple[int, int]: `(<right>, <bottom>)`
"""
return (self.right, self.bottom)
@property
def totals(self) -> tuple[int, int]:
"""Get total horizontal and vertical space.
Returns:
tuple[int, int]: `(<horizontal>, <vertical>)`
"""
top, right, bottom, left = self
return (left + right, top + bottom)
@property
def css(self) -> str:
"""Gets a string containing the spacing in CSS format.
Returns:
str: Spacing in CSS format.
"""
top, right, bottom, left = self
if top == right == bottom == left:
return f"{top}"
if (top, right) == (bottom, left):
return f"{top} {right}"
else:
return f"{top} {right} {bottom} {left}"
@classmethod
def unpack(cls, pad: SpacingDimensions) -> Spacing:
"""Unpack padding specified in CSS style.
Args:
pad (SpacingDimensions): An integer, or tuple of 1, 2, or 4 integers.
Raises:
ValueError: If `pad` is an invalid value.
Returns:
Spacing: New Spacing object.
"""
if isinstance(pad, int):
return cls(pad, pad, pad, pad)
pad_len = len(pad)
if pad_len == 1:
_pad = pad[0]
return cls(_pad, _pad, _pad, _pad)
if pad_len == 2:
pad_top, pad_right = cast(Tuple[int, int], pad)
return cls(pad_top, pad_right, pad_top, pad_right)
if pad_len == 4:
top, right, bottom, left = cast(Tuple[int, int, int, int], pad)
return cls(top, right, bottom, left)
raise ValueError(
f"1, 2 or 4 integers required for spacing properties; {pad_len} given"
)
@classmethod
def vertical(cls, amount: int) -> Spacing:
"""Construct a Spacing with a given amount of spacing on vertical edges,
and no horizontal spacing.
Args:
amount (int): The magnitude of spacing to apply to vertical edges
Returns:
Spacing: `Spacing(amount, 0, amount, 0)`
"""
return Spacing(amount, 0, amount, 0)
@classmethod
def horizontal(cls, amount: int) -> Spacing:
"""Construct a Spacing with a given amount of spacing on horizontal edges,
and no vertical spacing.
Args:
amount (int): The magnitude of spacing to apply to horizontal edges
Returns:
Spacing: `Spacing(0, amount, 0, amount)`
"""
return Spacing(0, amount, 0, amount)
@classmethod
def all(cls, amount: int) -> Spacing:
"""Construct a Spacing with a given amount of spacing on all edges.
Args:
amount (int): The magnitude of spacing to apply to all edges
Returns:
Spacing: `Spacing(amount, amount, amount, amount)`
"""
return Spacing(amount, amount, amount, amount)
def __add__(self, other: object) -> Spacing:
if isinstance(other, tuple):
top1, right1, bottom1, left1 = self
top2, right2, bottom2, left2 = other
return Spacing(
top1 + top2, right1 + right2, bottom1 + bottom2, left1 + left2
)
return NotImplemented
def __sub__(self, other: object) -> Spacing:
if isinstance(other, tuple):
top1, right1, bottom1, left1 = self
top2, right2, bottom2, left2 = other
return Spacing(
top1 - top2, right1 - right2, bottom1 - bottom2, left1 - left2
)
return NotImplemented
def grow_maximum(self, other: Spacing) -> Spacing:
"""Grow spacing with a maximum.
Args:
other (Spacing): Spacing object.
Returns:
Spacing: New spacing were the values are maximum of the two values.
"""
top, right, bottom, left = self
other_top, other_right, other_bottom, other_left = other
return Spacing(
max(top, other_top),
max(right, other_right),
max(bottom, other_bottom),
max(left, other_left),
)
NULL_OFFSET = Offset(0, 0)
Functions
def clamp(value: T, minimum: T, maximum: T) ‑> ~T
-
Adjust a value to it is not less than a minimum and not greater than a maximum value.
Args
value
:T
- A value.
minimum
:T
- Minimum value.
maximum
:T
- maximum value.
Returns
T
- New value that is not less than the minimum or greater than the maximum.
Expand source code
def clamp(value: T, minimum: T, maximum: T) -> T: """Adjust a value to it is not less than a minimum and not greater than a maximum value. Args: value (T): A value. minimum (T): Minimum value. maximum (T): maximum value. Returns: T: New value that is not less than the minimum or greater than the maximum. """ if minimum > maximum: maximum, minimum = minimum, maximum if value < minimum: return minimum elif value > maximum: return maximum else: return value
Classes
class Offset (x: int = 0, y: int = 0)
-
A cell offset defined by x and y coordinates. Offsets are typically relative to the top left of the terminal or other container.
Textual prefers the names
x
andy
, but you could considerx
to be the column andy
to be the row.Expand source code
class Offset(NamedTuple): """A cell offset defined by x and y coordinates. Offsets are typically relative to the top left of the terminal or other container. Textual prefers the names `x` and `y`, but you could consider `x` to be the _column_ and `y` to be the _row_. """ x: int = 0 """Offset in the x-axis (horizontal)""" y: int = 0 """Offset in the y-axis (vertical)""" @property def is_origin(self) -> bool: """Check if the point is at the origin (0, 0). Returns: bool: True if the offset is the origin. """ return self == (0, 0) @property def clamped(self) -> Offset: """Ensure x and y are above zero. Returns: Offset: New offset. """ x, y = self return Offset(0 if x < 0 else x, 0 if y < 0 else y) def __bool__(self) -> bool: return self != (0, 0) def __add__(self, other: object) -> Offset: if isinstance(other, tuple): _x, _y = self x, y = other return Offset(_x + x, _y + y) return NotImplemented def __sub__(self, other: object) -> Offset: if isinstance(other, tuple): _x, _y = self x, y = other return Offset(_x - x, _y - y) return NotImplemented def __mul__(self, other: object) -> Offset: if isinstance(other, (float, int)): x, y = self return Offset(int(x * other), int(y * other)) return NotImplemented def __neg__(self) -> Offset: x, y = self return Offset(-x, -y) def blend(self, destination: Offset, factor: float) -> Offset: """Blend (interpolate) to a new point. Args: destination (Point): Point where factor would be 1.0. factor (float): A value between 0 and 1.0. Returns: Point: A new point on a line between self and destination. """ x1, y1 = self x2, y2 = destination return Offset( int(x1 + (x2 - x1) * factor), int(y1 + (y2 - y1) * factor), ) def get_distance_to(self, other: Offset) -> float: """Get the distance to another offset. Args: other (Offset): An offset. Returns: float: Distance to other offset. """ x1, y1 = self x2, y2 = other distance = ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ** 0.5 return distance
Ancestors
- builtins.tuple
Instance variables
var clamped : Offset
-
Expand source code
@property def clamped(self) -> Offset: """Ensure x and y are above zero. Returns: Offset: New offset. """ x, y = self return Offset(0 if x < 0 else x, 0 if y < 0 else y)
var is_origin : bool
-
Check if the point is at the origin (0, 0).
Returns
bool
- True if the offset is the origin.
Expand source code
@property def is_origin(self) -> bool: """Check if the point is at the origin (0, 0). Returns: bool: True if the offset is the origin. """ return self == (0, 0)
var x : int
-
Offset in the x-axis (horizontal)
var y : int
-
Offset in the y-axis (vertical)
Methods
def blend(self, destination: Offset, factor: float) ‑> Offset
-
Blend (interpolate) to a new point.
Args
destination
:Point
- Point where factor would be 1.0.
factor
:float
- A value between 0 and 1.0.
Returns
Point
- A new point on a line between self and destination.
Expand source code
def blend(self, destination: Offset, factor: float) -> Offset: """Blend (interpolate) to a new point. Args: destination (Point): Point where factor would be 1.0. factor (float): A value between 0 and 1.0. Returns: Point: A new point on a line between self and destination. """ x1, y1 = self x2, y2 = destination return Offset( int(x1 + (x2 - x1) * factor), int(y1 + (y2 - y1) * factor), )
def get_distance_to(self, other: Offset) ‑> float
-
Get the distance to another offset.
Args
other
:Offset
- An offset.
Returns
float
- Distance to other offset.
Expand source code
def get_distance_to(self, other: Offset) -> float: """Get the distance to another offset. Args: other (Offset): An offset. Returns: float: Distance to other offset. """ x1, y1 = self x2, y2 = other distance = ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) ** 0.5 return distance
class Region (x: int = 0, y: int = 0, width: int = 0, height: int = 0)
-
Defines a rectangular region.
A Region consists of a coordinate (x and y) and dimensions (width and height).
(x, y) ┌────────────────────┐ ▲ │ │ │ │ │ │ │ │ height │ │ │ │ │ │ └────────────────────┘ ▼ ◀─────── width ──────▶
Expand source code
class Region(NamedTuple): """Defines a rectangular region. A Region consists of a coordinate (x and y) and dimensions (width and height). ``` (x, y) ┌────────────────────┐ ▲ │ │ │ │ │ │ │ │ height │ │ │ │ │ │ └────────────────────┘ ▼ ◀─────── width ──────▶ ``` """ x: int = 0 """Offset in the x-axis (horizontal).""" y: int = 0 """Offset in the y-axis (vertical).""" width: int = 0 """The width of the region.""" height: int = 0 """The height of the region.""" @classmethod def from_union(cls, regions: Collection[Region]) -> Region: """Create a Region from the union of other regions. Args: regions (Collection[Region]): One or more regions. Returns: Region: A Region that encloses all other regions. """ if not regions: raise ValueError("At least one region expected") min_x = min(regions, key=itemgetter(0)).x max_x = max(regions, key=attrgetter("right")).right min_y = min(regions, key=itemgetter(1)).y max_y = max(regions, key=attrgetter("bottom")).bottom return cls(min_x, min_y, max_x - min_x, max_y - min_y) @classmethod def from_corners(cls, x1: int, y1: int, x2: int, y2: int) -> Region: """Construct a Region form the top left and bottom right corners. Args: x1 (int): Top left x. y1 (int): Top left y. x2 (int): Bottom right x. y2 (int): Bottom right y. Returns: Region: A new region. """ return cls(x1, y1, x2 - x1, y2 - y1) @classmethod def from_offset(cls, offset: tuple[int, int], size: tuple[int, int]) -> Region: """Create a region from offset and size. Args: offset (Point): Offset (top left point). size (tuple[int, int]): Dimensions of region. Returns: Region: A region instance. """ x, y = offset width, height = size return cls(x, y, width, height) @classmethod def get_scroll_to_visible( cls, window_region: Region, region: Region, *, top: bool = False ) -> Offset: """Calculate the smallest offset required to translate a window so that it contains another region. This method is used to calculate the required offset to scroll something in to view. Args: window_region (Region): The window region. region (Region): The region to move inside the window. top (bool, optional): Get offset to top of window. Defaults to False Returns: Offset: An offset required to add to region to move it inside window_region. """ if region in window_region: # Region is already inside the window, so no need to move it. return Offset(0, 0) window_left, window_top, window_right, window_bottom = window_region.corners region = region.crop_size(window_region.size) left, top_, right, bottom = region.corners delta_x = delta_y = 0 if not ( (window_right > left >= window_left) and (window_right > right >= window_left) ): # The region does not fit # The window needs to scroll on the X axis to bring region in to view delta_x = min( left - window_left, left - (window_right - region.width), key=abs, ) if not ( (window_bottom > top_ >= window_top) and (window_bottom > bottom >= window_top) ): # The window needs to scroll on the Y axis to bring region in to view if top: delta_y = top_ - window_top else: delta_y = min( top_ - window_top, top_ - (window_bottom - region.height), key=abs, ) return Offset(delta_x, delta_y) def __bool__(self) -> bool: """A Region is considered False when it has no area.""" _, _, width, height = self return width * height > 0 @property def column_span(self) -> tuple[int, int]: """Get the start and end columns (x coord). The end value is exclusive. Returns: tuple[int, int]: Pair of x coordinates (column numbers). """ return (self.x, self.x + self.width) @property def line_span(self) -> tuple[int, int]: """Get the start and end line number (y coord). The end value is exclusive. Returns: tuple[int, int]: Pair of y coordinates (line numbers). """ return (self.y, self.y + self.height) @property def right(self) -> int: """Maximum X value (non inclusive). Returns: int: x coordinate. """ return self.x + self.width @property def bottom(self) -> int: """Maximum Y value (non inclusive). Returns: int: y coordinate. """ return self.y + self.height @property def area(self) -> int: """Get the area within the region. Returns: int: Area covered by this region. """ return self.width * self.height @property def offset(self) -> Offset: """Get the start point of the region. Returns: Offset: Top left offset. """ return Offset(self.x, self.y) @property def bottom_left(self) -> Offset: """Bottom left offset of the region. Returns: Offset: Bottom left offset. """ x, y, _width, height = self return Offset(x, y + height) @property def top_right(self) -> Offset: """Top right offset of the region. Returns: Offset: Top right. """ x, y, width, _height = self return Offset(x + width, y) @property def bottom_right(self) -> Offset: """Bottom right of the region. Returns: Offset: Bottom right. """ x, y, width, height = self return Offset(x + width, y + height) @property def size(self) -> Size: """Get the size of the region. Returns: Size: Size of the region. """ return Size(self.width, self.height) @property def corners(self) -> tuple[int, int, int, int]: """Get the top left and bottom right coordinates as a tuple of integers. Returns: tuple[int, int, int, int]: A tuple of `(<left>, <top>, <right>, <bottom>)`. """ x, y, width, height = self return x, y, x + width, y + height @property def column_range(self) -> range: """A range object for X coordinates.""" return range(self.x, self.x + self.width) @property def line_range(self) -> range: """A range object for Y coordinates.""" return range(self.y, self.y + self.height) @property def reset_offset(self) -> Region: """An region of the same size at (0, 0). Returns: Region: reset region. """ _, _, width, height = self return Region(0, 0, width, height) def __add__(self, other: object) -> Region: if isinstance(other, tuple): ox, oy = other x, y, width, height = self return Region(x + ox, y + oy, width, height) return NotImplemented def __sub__(self, other: object) -> Region: if isinstance(other, tuple): ox, oy = other x, y, width, height = self return Region(x - ox, y - oy, width, height) return NotImplemented def at_offset(self, offset: tuple[int, int]) -> Region: """Get a new Region with the same size at a given offset. Args: offset (tuple[int, int]): An offset. Returns: Region: New Region with adjusted offset. """ x, y = offset _x, _y, width, height = self return Region(x, y, width, height) def crop_size(self, size: tuple[int, int]) -> Region: """Get a region with the same offset, with a size no larger than `size`. Args: size (tuple[int, int]): Maximum width and height (WIDTH, HEIGHT). Returns: Region: New region that could fit within `size`. """ x, y, width1, height1 = self width2, height2 = size return Region(x, y, min(width1, width2), min(height1, height2)) def expand(self, size: tuple[int, int]) -> Region: """Increase the size of the region by adding a border. Args: size (tuple[int, int]): Additional width and height. Returns: Region: A new region. """ expand_width, expand_height = size x, y, width, height = self return Region( x - expand_width, y - expand_height, width + expand_width * 2, height + expand_height * 2, ) def clip_size(self, size: tuple[int, int]) -> Region: """Clip the size to fit within minimum values. Args: size (tuple[int, int]): Maximum width and height. Returns: Region: No region, not bigger than size. """ x, y, width, height = self max_width, max_height = size return Region(x, y, min(width, max_width), min(height, max_height)) @lru_cache(maxsize=1024) def overlaps(self, other: Region) -> bool: """Check if another region overlaps this region. Args: other (Region): A Region. Returns: bool: True if other region shares any cells with this region. """ x, y, x2, y2 = self.corners ox, oy, ox2, oy2 = other.corners return ((x2 > ox >= x) or (x2 > ox2 > x) or (ox < x and ox2 >= x2)) and ( (y2 > oy >= y) or (y2 > oy2 > y) or (oy < y and oy2 >= y2) ) def contains(self, x: int, y: int) -> bool: """Check if a point is in the region. Args: x (int): X coordinate. y (int): Y coordinate. Returns: bool: True if the point is within the region. """ self_x, self_y, width, height = self return (self_x + width > x >= self_x) and (self_y + height > y >= self_y) def contains_point(self, point: tuple[int, int]) -> bool: """Check if a point is in the region. Args: point (tuple[int, int]): A tuple of x and y coordinates. Returns: bool: True if the point is within the region. """ x1, y1, x2, y2 = self.corners try: ox, oy = point except Exception: raise TypeError(f"a tuple of two integers is required, not {point!r}") return (x2 > ox >= x1) and (y2 > oy >= y1) @lru_cache(maxsize=1024) def contains_region(self, other: Region) -> bool: """Check if a region is entirely contained within this region. Args: other (Region): A region. Returns: bool: True if the other region fits perfectly within this region. """ x1, y1, x2, y2 = self.corners ox, oy, ox2, oy2 = other.corners return ( (x2 >= ox >= x1) and (y2 >= oy >= y1) and (x2 >= ox2 >= x1) and (y2 >= oy2 >= y1) ) def translate(self, offset: tuple[int, int]) -> Region: """Move the offset of the Region. Args: offset (tuple[int, int]): Offset to add to region. Returns: Region: A new region shifted by (x, y) """ self_x, self_y, width, height = self offset_x, offset_y = offset return Region(self_x + offset_x, self_y + offset_y, width, height) @lru_cache(maxsize=4096) def __contains__(self, other: Any) -> bool: """Check if a point is in this region.""" if isinstance(other, Region): return self.contains_region(other) else: try: return self.contains_point(other) except TypeError: return False def clip(self, width: int, height: int) -> Region: """Clip this region to fit within width, height. Args: width (int): Width of bounds. height (int): Height of bounds. Returns: Region: Clipped region. """ x1, y1, x2, y2 = self.corners _clamp = clamp new_region = Region.from_corners( _clamp(x1, 0, width), _clamp(y1, 0, height), _clamp(x2, 0, width), _clamp(y2, 0, height), ) return new_region def grow(self, margin: tuple[int, int, int, int]) -> Region: """Grow a region by adding spacing. Args: margin (tuple[int, int, in, int]): Grow space by `(<top>, <right>, <bottom>, <left>)`. Returns: Region: New region. """ top, right, bottom, left = margin x, y, width, height = self return Region( x=x - left, y=y - top, width=max(0, width + left + right), height=max(0, height + top + bottom), ) def shrink(self, margin: tuple[int, int, int, int]) -> Region: """Shrink a region by subtracting spacing. Args: margin (tuple[int, int, int, int]): Shrink space by `(<top>, <right>, <bottom>, <left>)`. Returns: Region: The new, smaller region. """ top, right, bottom, left = margin x, y, width, height = self return Region( x=x + left, y=y + top, width=max(0, width - (left + right)), height=max(0, height - (top + bottom)), ) @lru_cache(maxsize=4096) def intersection(self, region: Region) -> Region: """Get the overlapping portion of the two regions. Args: region (Region): A region that overlaps this region. Returns: Region: A new region that covers when the two regions overlap. """ # Unrolled because this method is used a lot x1, y1, w1, h1 = self cx1, cy1, w2, h2 = region x2 = x1 + w1 y2 = y1 + h1 cx2 = cx1 + w2 cy2 = cy1 + h2 rx1 = cx2 if x1 > cx2 else (cx1 if x1 < cx1 else x1) ry1 = cy2 if y1 > cy2 else (cy1 if y1 < cy1 else y1) rx2 = cx2 if x2 > cx2 else (cx1 if x2 < cx1 else x2) ry2 = cy2 if y2 > cy2 else (cy1 if y2 < cy1 else y2) return Region(rx1, ry1, rx2 - rx1, ry2 - ry1) @lru_cache(maxsize=4096) def union(self, region: Region) -> Region: """Get the smallest region that contains both regions. Args: region (Region): Another region. Returns: Region: An optimally sized region to cover both regions. """ x1, y1, x2, y2 = self.corners ox1, oy1, ox2, oy2 = region.corners union_region = self.from_corners( min(x1, ox1), min(y1, oy1), max(x2, ox2), max(y2, oy2) ) return union_region @lru_cache(maxsize=1024) def split(self, cut_x: int, cut_y: int) -> tuple[Region, Region, Region, Region]: """Split a region in to 4 from given x and y offsets (cuts). ``` cut_x ↓ ┌────────┐ ┌───┐ │ │ │ │ │ 0 │ │ 1 │ │ │ │ │ cut_y → └────────┘ └───┘ ┌────────┐ ┌───┐ │ 2 │ │ 3 │ └────────┘ └───┘ ``` Args: cut_x (int): Offset from self.x where the cut should be made. If negative, the cut is taken from the right edge. cut_y (int): Offset from self.y where the cut should be made. If negative, the cut is taken from the lower edge. Returns: tuple[Region, Region, Region, Region]: Four new regions which add up to the original (self). """ x, y, width, height = self if cut_x < 0: cut_x = width + cut_x if cut_y < 0: cut_y = height + cut_y _Region = Region return ( _Region(x, y, cut_x, cut_y), _Region(x + cut_x, y, width - cut_x, cut_y), _Region(x, y + cut_y, cut_x, height - cut_y), _Region(x + cut_x, y + cut_y, width - cut_x, height - cut_y), ) @lru_cache(maxsize=1024) def split_vertical(self, cut: int) -> tuple[Region, Region]: """Split a region in to two, from a given x offset. ``` cut ↓ ┌────────┐┌───┐ │ 0 ││ 1 │ │ ││ │ └────────┘└───┘ ``` Args: cut (int): An offset from self.x where the cut should be made. If cut is negative, it is taken from the right edge. Returns: tuple[Region, Region]: Two regions, which add up to the original (self). """ x, y, width, height = self if cut < 0: cut = width + cut return ( Region(x, y, cut, height), Region(x + cut, y, width - cut, height), ) @lru_cache(maxsize=1024) def split_horizontal(self, cut: int) -> tuple[Region, Region]: """Split a region in to two, from a given x offset. ``` ┌─────────┐ │ 0 │ │ │ cut → └─────────┘ ┌─────────┐ │ 1 │ └─────────┘ ``` Args: cut (int): An offset from self.x where the cut should be made. May be negative, for the offset to start from the right edge. Returns: tuple[Region, Region]: Two regions, which add up to the original (self). """ x, y, width, height = self if cut < 0: cut = height + cut return ( Region(x, y, width, cut), Region(x, y + cut, width, height - cut), )
Ancestors
- builtins.tuple
Static methods
def from_corners(x1: int, y1: int, x2: int, y2: int) ‑> Region
-
Construct a Region form the top left and bottom right corners.
Args
x1
:int
- Top left x.
y1
:int
- Top left y.
x2
:int
- Bottom right x.
y2
:int
- Bottom right y.
Returns
Region
- A new region.
Expand source code
@classmethod def from_corners(cls, x1: int, y1: int, x2: int, y2: int) -> Region: """Construct a Region form the top left and bottom right corners. Args: x1 (int): Top left x. y1 (int): Top left y. x2 (int): Bottom right x. y2 (int): Bottom right y. Returns: Region: A new region. """ return cls(x1, y1, x2 - x1, y2 - y1)
def from_offset(offset: tuple[int, int], size: tuple[int, int]) ‑> Region
-
Create a region from offset and size.
Args
offset
:Point
- Offset (top left point).
size
:tuple[int, int]
- Dimensions of region.
Returns
Region
- A region instance.
Expand source code
@classmethod def from_offset(cls, offset: tuple[int, int], size: tuple[int, int]) -> Region: """Create a region from offset and size. Args: offset (Point): Offset (top left point). size (tuple[int, int]): Dimensions of region. Returns: Region: A region instance. """ x, y = offset width, height = size return cls(x, y, width, height)
def from_union(regions: Collection[Region]) ‑> Region
-
Create a Region from the union of other regions.
Args
regions
:Collection[Region]
- One or more regions.
Returns
Region
- A Region that encloses all other regions.
Expand source code
@classmethod def from_union(cls, regions: Collection[Region]) -> Region: """Create a Region from the union of other regions. Args: regions (Collection[Region]): One or more regions. Returns: Region: A Region that encloses all other regions. """ if not regions: raise ValueError("At least one region expected") min_x = min(regions, key=itemgetter(0)).x max_x = max(regions, key=attrgetter("right")).right min_y = min(regions, key=itemgetter(1)).y max_y = max(regions, key=attrgetter("bottom")).bottom return cls(min_x, min_y, max_x - min_x, max_y - min_y)
def get_scroll_to_visible(window_region: Region, region: Region, *, top: bool = False) ‑> Offset
-
Calculate the smallest offset required to translate a window so that it contains another region.
This method is used to calculate the required offset to scroll something in to view.
Args
window_region
:Region
- The window region.
region
:Region
- The region to move inside the window.
top
:bool
, optional- Get offset to top of window. Defaults to False
Returns
Offset
- An offset required to add to region to move it inside window_region.
Expand source code
@classmethod def get_scroll_to_visible( cls, window_region: Region, region: Region, *, top: bool = False ) -> Offset: """Calculate the smallest offset required to translate a window so that it contains another region. This method is used to calculate the required offset to scroll something in to view. Args: window_region (Region): The window region. region (Region): The region to move inside the window. top (bool, optional): Get offset to top of window. Defaults to False Returns: Offset: An offset required to add to region to move it inside window_region. """ if region in window_region: # Region is already inside the window, so no need to move it. return Offset(0, 0) window_left, window_top, window_right, window_bottom = window_region.corners region = region.crop_size(window_region.size) left, top_, right, bottom = region.corners delta_x = delta_y = 0 if not ( (window_right > left >= window_left) and (window_right > right >= window_left) ): # The region does not fit # The window needs to scroll on the X axis to bring region in to view delta_x = min( left - window_left, left - (window_right - region.width), key=abs, ) if not ( (window_bottom > top_ >= window_top) and (window_bottom > bottom >= window_top) ): # The window needs to scroll on the Y axis to bring region in to view if top: delta_y = top_ - window_top else: delta_y = min( top_ - window_top, top_ - (window_bottom - region.height), key=abs, ) return Offset(delta_x, delta_y)
Instance variables
var area : int
-
Get the area within the region.
Returns
int
- Area covered by this region.
Expand source code
@property def area(self) -> int: """Get the area within the region. Returns: int: Area covered by this region. """ return self.width * self.height
var bottom : int
-
Maximum Y value (non inclusive).
Returns
int
- y coordinate.
Expand source code
@property def bottom(self) -> int: """Maximum Y value (non inclusive). Returns: int: y coordinate. """ return self.y + self.height
var bottom_left : Offset
-
Expand source code
@property def bottom_left(self) -> Offset: """Bottom left offset of the region. Returns: Offset: Bottom left offset. """ x, y, _width, height = self return Offset(x, y + height)
var bottom_right : Offset
-
Expand source code
@property def bottom_right(self) -> Offset: """Bottom right of the region. Returns: Offset: Bottom right. """ x, y, width, height = self return Offset(x + width, y + height)
var column_range : range
-
A range object for X coordinates.
Expand source code
@property def column_range(self) -> range: """A range object for X coordinates.""" return range(self.x, self.x + self.width)
var column_span : tuple[int, int]
-
Get the start and end columns (x coord).
The end value is exclusive.
Returns
tuple[int, int]
- Pair of x coordinates (column numbers).
Expand source code
@property def column_span(self) -> tuple[int, int]: """Get the start and end columns (x coord). The end value is exclusive. Returns: tuple[int, int]: Pair of x coordinates (column numbers). """ return (self.x, self.x + self.width)
var corners : tuple[int, int, int, int]
-
Get the top left and bottom right coordinates as a tuple of integers.
Returns
tuple[int, int, int, int]
- A tuple of
(<left>, <top>, <right>, <bottom>)
.
Expand source code
@property def corners(self) -> tuple[int, int, int, int]: """Get the top left and bottom right coordinates as a tuple of integers. Returns: tuple[int, int, int, int]: A tuple of `(<left>, <top>, <right>, <bottom>)`. """ x, y, width, height = self return x, y, x + width, y + height
var height : int
-
The height of the region.
var line_range : range
-
A range object for Y coordinates.
Expand source code
@property def line_range(self) -> range: """A range object for Y coordinates.""" return range(self.y, self.y + self.height)
var line_span : tuple[int, int]
-
Get the start and end line number (y coord).
The end value is exclusive.
Returns
tuple[int, int]
- Pair of y coordinates (line numbers).
Expand source code
@property def line_span(self) -> tuple[int, int]: """Get the start and end line number (y coord). The end value is exclusive. Returns: tuple[int, int]: Pair of y coordinates (line numbers). """ return (self.y, self.y + self.height)
var offset : Offset
-
Expand source code
@property def offset(self) -> Offset: """Get the start point of the region. Returns: Offset: Top left offset. """ return Offset(self.x, self.y)
var reset_offset : Region
-
Expand source code
@property def reset_offset(self) -> Region: """An region of the same size at (0, 0). Returns: Region: reset region. """ _, _, width, height = self return Region(0, 0, width, height)
var right : int
-
Maximum X value (non inclusive).
Returns
int
- x coordinate.
Expand source code
@property def right(self) -> int: """Maximum X value (non inclusive). Returns: int: x coordinate. """ return self.x + self.width
var size : Size
-
Expand source code
@property def size(self) -> Size: """Get the size of the region. Returns: Size: Size of the region. """ return Size(self.width, self.height)
var top_right : Offset
-
Expand source code
@property def top_right(self) -> Offset: """Top right offset of the region. Returns: Offset: Top right. """ x, y, width, _height = self return Offset(x + width, y)
var width : int
-
The width of the region.
var x : int
-
Offset in the x-axis (horizontal).
var y : int
-
Offset in the y-axis (vertical).
Methods
def at_offset(self, offset: tuple[int, int]) ‑> Region
-
Get a new Region with the same size at a given offset.
Args
offset
:tuple[int, int]
- An offset.
Returns
Region
- New Region with adjusted offset.
Expand source code
def at_offset(self, offset: tuple[int, int]) -> Region: """Get a new Region with the same size at a given offset. Args: offset (tuple[int, int]): An offset. Returns: Region: New Region with adjusted offset. """ x, y = offset _x, _y, width, height = self return Region(x, y, width, height)
def clip(self, width: int, height: int) ‑> Region
-
Clip this region to fit within width, height.
Args
width
:int
- Width of bounds.
height
:int
- Height of bounds.
Returns
Region
- Clipped region.
Expand source code
def clip(self, width: int, height: int) -> Region: """Clip this region to fit within width, height. Args: width (int): Width of bounds. height (int): Height of bounds. Returns: Region: Clipped region. """ x1, y1, x2, y2 = self.corners _clamp = clamp new_region = Region.from_corners( _clamp(x1, 0, width), _clamp(y1, 0, height), _clamp(x2, 0, width), _clamp(y2, 0, height), ) return new_region
def clip_size(self, size: tuple[int, int]) ‑> Region
-
Clip the size to fit within minimum values.
Args
size
:tuple[int, int]
- Maximum width and height.
Returns
Region
- No region, not bigger than size.
Expand source code
def clip_size(self, size: tuple[int, int]) -> Region: """Clip the size to fit within minimum values. Args: size (tuple[int, int]): Maximum width and height. Returns: Region: No region, not bigger than size. """ x, y, width, height = self max_width, max_height = size return Region(x, y, min(width, max_width), min(height, max_height))
def contains(self, x: int, y: int) ‑> bool
-
Check if a point is in the region.
Args
x
:int
- X coordinate.
y
:int
- Y coordinate.
Returns
bool
- True if the point is within the region.
Expand source code
def contains(self, x: int, y: int) -> bool: """Check if a point is in the region. Args: x (int): X coordinate. y (int): Y coordinate. Returns: bool: True if the point is within the region. """ self_x, self_y, width, height = self return (self_x + width > x >= self_x) and (self_y + height > y >= self_y)
def contains_point(self, point: tuple[int, int]) ‑> bool
-
Check if a point is in the region.
Args
point
:tuple[int, int]
- A tuple of x and y coordinates.
Returns
bool
- True if the point is within the region.
Expand source code
def contains_point(self, point: tuple[int, int]) -> bool: """Check if a point is in the region. Args: point (tuple[int, int]): A tuple of x and y coordinates. Returns: bool: True if the point is within the region. """ x1, y1, x2, y2 = self.corners try: ox, oy = point except Exception: raise TypeError(f"a tuple of two integers is required, not {point!r}") return (x2 > ox >= x1) and (y2 > oy >= y1)
def contains_region(self, other: Region) ‑> bool
-
Check if a region is entirely contained within this region.
Args
other
:Region
- A region.
Returns
bool
- True if the other region fits perfectly within this region.
Expand source code
@lru_cache(maxsize=1024) def contains_region(self, other: Region) -> bool: """Check if a region is entirely contained within this region. Args: other (Region): A region. Returns: bool: True if the other region fits perfectly within this region. """ x1, y1, x2, y2 = self.corners ox, oy, ox2, oy2 = other.corners return ( (x2 >= ox >= x1) and (y2 >= oy >= y1) and (x2 >= ox2 >= x1) and (y2 >= oy2 >= y1) )
def crop_size(self, size: tuple[int, int]) ‑> Region
-
Get a region with the same offset, with a size no larger than
size
.Args
size
:tuple[int, int]
- Maximum width and height (WIDTH, HEIGHT).
Returns
Region
- New region that could fit within
size
.
Expand source code
def crop_size(self, size: tuple[int, int]) -> Region: """Get a region with the same offset, with a size no larger than `size`. Args: size (tuple[int, int]): Maximum width and height (WIDTH, HEIGHT). Returns: Region: New region that could fit within `size`. """ x, y, width1, height1 = self width2, height2 = size return Region(x, y, min(width1, width2), min(height1, height2))
def expand(self, size: tuple[int, int]) ‑> Region
-
Increase the size of the region by adding a border.
Args
size
:tuple[int, int]
- Additional width and height.
Returns
Region
- A new region.
Expand source code
def expand(self, size: tuple[int, int]) -> Region: """Increase the size of the region by adding a border. Args: size (tuple[int, int]): Additional width and height. Returns: Region: A new region. """ expand_width, expand_height = size x, y, width, height = self return Region( x - expand_width, y - expand_height, width + expand_width * 2, height + expand_height * 2, )
def grow(self, margin: tuple[int, int, int, int]) ‑> Region
-
Grow a region by adding spacing.
Args
margin
:tuple[int, int, in, int]
- Grow space by
(<top>, <right>, <bottom>, <left>)
.
Returns
Region
- New region.
Expand source code
def grow(self, margin: tuple[int, int, int, int]) -> Region: """Grow a region by adding spacing. Args: margin (tuple[int, int, in, int]): Grow space by `(<top>, <right>, <bottom>, <left>)`. Returns: Region: New region. """ top, right, bottom, left = margin x, y, width, height = self return Region( x=x - left, y=y - top, width=max(0, width + left + right), height=max(0, height + top + bottom), )
def intersection(self, region: Region) ‑> Region
-
Get the overlapping portion of the two regions.
Args
region
:Region
- A region that overlaps this region.
Returns
Region
- A new region that covers when the two regions overlap.
Expand source code
@lru_cache(maxsize=4096) def intersection(self, region: Region) -> Region: """Get the overlapping portion of the two regions. Args: region (Region): A region that overlaps this region. Returns: Region: A new region that covers when the two regions overlap. """ # Unrolled because this method is used a lot x1, y1, w1, h1 = self cx1, cy1, w2, h2 = region x2 = x1 + w1 y2 = y1 + h1 cx2 = cx1 + w2 cy2 = cy1 + h2 rx1 = cx2 if x1 > cx2 else (cx1 if x1 < cx1 else x1) ry1 = cy2 if y1 > cy2 else (cy1 if y1 < cy1 else y1) rx2 = cx2 if x2 > cx2 else (cx1 if x2 < cx1 else x2) ry2 = cy2 if y2 > cy2 else (cy1 if y2 < cy1 else y2) return Region(rx1, ry1, rx2 - rx1, ry2 - ry1)
def overlaps(self, other: Region) ‑> bool
-
Check if another region overlaps this region.
Args
other
:Region
- A Region.
Returns
bool
- True if other region shares any cells with this region.
Expand source code
@lru_cache(maxsize=1024) def overlaps(self, other: Region) -> bool: """Check if another region overlaps this region. Args: other (Region): A Region. Returns: bool: True if other region shares any cells with this region. """ x, y, x2, y2 = self.corners ox, oy, ox2, oy2 = other.corners return ((x2 > ox >= x) or (x2 > ox2 > x) or (ox < x and ox2 >= x2)) and ( (y2 > oy >= y) or (y2 > oy2 > y) or (oy < y and oy2 >= y2) )
def shrink(self, margin: tuple[int, int, int, int]) ‑> Region
-
Shrink a region by subtracting spacing.
Args
margin
:tuple[int, int, int, int]
- Shrink space by
(<top>, <right>, <bottom>, <left>)
.
Returns
Region
- The new, smaller region.
Expand source code
def shrink(self, margin: tuple[int, int, int, int]) -> Region: """Shrink a region by subtracting spacing. Args: margin (tuple[int, int, int, int]): Shrink space by `(<top>, <right>, <bottom>, <left>)`. Returns: Region: The new, smaller region. """ top, right, bottom, left = margin x, y, width, height = self return Region( x=x + left, y=y + top, width=max(0, width - (left + right)), height=max(0, height - (top + bottom)), )
def split(self, cut_x: int, cut_y: int) ‑> tuple[Region, Region, Region, Region]
-
Split a region in to 4 from given x and y offsets (cuts).
cut_x ↓ ┌────────┐ ┌───┐ │ │ │ │ │ 0 │ │ 1 │ │ │ │ │ cut_y → └────────┘ └───┘ ┌────────┐ ┌───┐ │ 2 │ │ 3 │ └────────┘ └───┘
Args
cut_x
:int
- Offset from self.x where the cut should be made. If negative, the cut is taken from the right edge.
cut_y
:int
- Offset from self.y where the cut should be made. If negative, the cut is taken from the lower edge.
Returns
Expand source code
@lru_cache(maxsize=1024) def split(self, cut_x: int, cut_y: int) -> tuple[Region, Region, Region, Region]: """Split a region in to 4 from given x and y offsets (cuts). ``` cut_x ↓ ┌────────┐ ┌───┐ │ │ │ │ │ 0 │ │ 1 │ │ │ │ │ cut_y → └────────┘ └───┘ ┌────────┐ ┌───┐ │ 2 │ │ 3 │ └────────┘ └───┘ ``` Args: cut_x (int): Offset from self.x where the cut should be made. If negative, the cut is taken from the right edge. cut_y (int): Offset from self.y where the cut should be made. If negative, the cut is taken from the lower edge. Returns: tuple[Region, Region, Region, Region]: Four new regions which add up to the original (self). """ x, y, width, height = self if cut_x < 0: cut_x = width + cut_x if cut_y < 0: cut_y = height + cut_y _Region = Region return ( _Region(x, y, cut_x, cut_y), _Region(x + cut_x, y, width - cut_x, cut_y), _Region(x, y + cut_y, cut_x, height - cut_y), _Region(x + cut_x, y + cut_y, width - cut_x, height - cut_y), )
def split_horizontal(self, cut: int) ‑> tuple[Region, Region]
-
Split a region in to two, from a given x offset.
┌─────────┐ │ 0 │ │ │ cut → └─────────┘ ┌─────────┐ │ 1 │ └─────────┘
Args
cut
:int
- An offset from self.x where the cut should be made. May be negative, for the offset to start from the right edge.
Returns
Expand source code
@lru_cache(maxsize=1024) def split_horizontal(self, cut: int) -> tuple[Region, Region]: """Split a region in to two, from a given x offset. ``` ┌─────────┐ │ 0 │ │ │ cut → └─────────┘ ┌─────────┐ │ 1 │ └─────────┘ ``` Args: cut (int): An offset from self.x where the cut should be made. May be negative, for the offset to start from the right edge. Returns: tuple[Region, Region]: Two regions, which add up to the original (self). """ x, y, width, height = self if cut < 0: cut = height + cut return ( Region(x, y, width, cut), Region(x, y + cut, width, height - cut), )
def split_vertical(self, cut: int) ‑> tuple[Region, Region]
-
Split a region in to two, from a given x offset.
cut ↓ ┌────────┐┌───┐ │ 0 ││ 1 │ │ ││ │ └────────┘└───┘
Args
cut
:int
- An offset from self.x where the cut should be made. If cut is negative, it is taken from the right edge.
Returns
Expand source code
@lru_cache(maxsize=1024) def split_vertical(self, cut: int) -> tuple[Region, Region]: """Split a region in to two, from a given x offset. ``` cut ↓ ┌────────┐┌───┐ │ 0 ││ 1 │ │ ││ │ └────────┘└───┘ ``` Args: cut (int): An offset from self.x where the cut should be made. If cut is negative, it is taken from the right edge. Returns: tuple[Region, Region]: Two regions, which add up to the original (self). """ x, y, width, height = self if cut < 0: cut = width + cut return ( Region(x, y, cut, height), Region(x + cut, y, width - cut, height), )
def translate(self, offset: tuple[int, int]) ‑> Region
-
Move the offset of the Region.
Args
offset
:tuple[int, int]
- Offset to add to region.
Returns
Region
- A new region shifted by (x, y)
Expand source code
def translate(self, offset: tuple[int, int]) -> Region: """Move the offset of the Region. Args: offset (tuple[int, int]): Offset to add to region. Returns: Region: A new region shifted by (x, y) """ self_x, self_y, width, height = self offset_x, offset_y = offset return Region(self_x + offset_x, self_y + offset_y, width, height)
def union(self, region: Region) ‑> Region
-
Get the smallest region that contains both regions.
Args
region
:Region
- Another region.
Returns
Region
- An optimally sized region to cover both regions.
Expand source code
@lru_cache(maxsize=4096) def union(self, region: Region) -> Region: """Get the smallest region that contains both regions. Args: region (Region): Another region. Returns: Region: An optimally sized region to cover both regions. """ x1, y1, x2, y2 = self.corners ox1, oy1, ox2, oy2 = region.corners union_region = self.from_corners( min(x1, ox1), min(y1, oy1), max(x2, ox2), max(y2, oy2) ) return union_region
class Size (width: int = 0, height: int = 0)
-
The dimensions of a rectangular region.
Expand source code
class Size(NamedTuple): """The dimensions of a rectangular region.""" width: int = 0 """The width in cells.""" height: int = 0 """The height in cells.""" def __bool__(self) -> bool: """A Size is Falsy if it has area 0.""" return self.width * self.height != 0 @property def area(self) -> int: """Get the area of the size. Returns: int: Area in cells. """ return self.width * self.height @property def region(self) -> Region: """Get a region of the same size. Returns: Region: A region with the same size at (0, 0). """ width, height = self return Region(0, 0, width, height) @property def line_range(self) -> range: """Get a range covering lines. Returns: range: A builtin range object. """ return range(self.height) def __add__(self, other: object) -> Size: if isinstance(other, tuple): width, height = self width2, height2 = other return Size(max(0, width + width2), max(0, height + height2)) return NotImplemented def __sub__(self, other: object) -> Size: if isinstance(other, tuple): width, height = self width2, height2 = other return Size(max(0, width - width2), max(0, height - height2)) return NotImplemented def contains(self, x: int, y: int) -> bool: """Check if a point is in area defined by the size. Args: x (int): X coordinate. y (int): Y coordinate. Returns: bool: True if the point is within the region. """ width, height = self return width > x >= 0 and height > y >= 0 def contains_point(self, point: tuple[int, int]) -> bool: """Check if a point is in the area defined by the size. Args: point (tuple[int, int]): A tuple of x and y coordinates. Returns: bool: True if the point is within the region. """ x, y = point width, height = self return width > x >= 0 and height > y >= 0 def __contains__(self, other: Any) -> bool: try: x, y = other except Exception: raise TypeError( "Dimensions.__contains__ requires an iterable of two integers" ) width, height = self return width > x >= 0 and height > y >= 0
Ancestors
- builtins.tuple
Instance variables
var area : int
-
Get the area of the size.
Returns
int
- Area in cells.
Expand source code
@property def area(self) -> int: """Get the area of the size. Returns: int: Area in cells. """ return self.width * self.height
var height : int
-
The height in cells.
var line_range : range
-
Get a range covering lines.
Returns
range
- A builtin range object.
Expand source code
@property def line_range(self) -> range: """Get a range covering lines. Returns: range: A builtin range object. """ return range(self.height)
var region : Region
-
Expand source code
@property def region(self) -> Region: """Get a region of the same size. Returns: Region: A region with the same size at (0, 0). """ width, height = self return Region(0, 0, width, height)
var width : int
-
The width in cells.
Methods
def contains(self, x: int, y: int) ‑> bool
-
Check if a point is in area defined by the size.
Args
x
:int
- X coordinate.
y
:int
- Y coordinate.
Returns
bool
- True if the point is within the region.
Expand source code
def contains(self, x: int, y: int) -> bool: """Check if a point is in area defined by the size. Args: x (int): X coordinate. y (int): Y coordinate. Returns: bool: True if the point is within the region. """ width, height = self return width > x >= 0 and height > y >= 0
def contains_point(self, point: tuple[int, int]) ‑> bool
-
Check if a point is in the area defined by the size.
Args
point
:tuple[int, int]
- A tuple of x and y coordinates.
Returns
bool
- True if the point is within the region.
Expand source code
def contains_point(self, point: tuple[int, int]) -> bool: """Check if a point is in the area defined by the size. Args: point (tuple[int, int]): A tuple of x and y coordinates. Returns: bool: True if the point is within the region. """ x, y = point width, height = self return width > x >= 0 and height > y >= 0
class Spacing (top: int = 0, right: int = 0, bottom: int = 0, left: int = 0)
-
The spacing around a renderable.
Expand source code
class Spacing(NamedTuple): """The spacing around a renderable.""" top: int = 0 """Space from the top of a region.""" right: int = 0 """Space from the left of a region.""" bottom: int = 0 """Space from the bottom of a region.""" left: int = 0 """Space from the left of a region.""" def __bool__(self) -> bool: return self != (0, 0, 0, 0) @property def width(self) -> int: """Total space in width. Returns: int: Width. """ return self.left + self.right @property def height(self) -> int: """Total space in height. Returns: int: Height. """ return self.top + self.bottom @property def top_left(self) -> tuple[int, int]: """Top left space. Returns: tuple[int, int]: `(<left>, <top>)` """ return (self.left, self.top) @property def bottom_right(self) -> tuple[int, int]: """Bottom right space. Returns: tuple[int, int]: `(<right>, <bottom>)` """ return (self.right, self.bottom) @property def totals(self) -> tuple[int, int]: """Get total horizontal and vertical space. Returns: tuple[int, int]: `(<horizontal>, <vertical>)` """ top, right, bottom, left = self return (left + right, top + bottom) @property def css(self) -> str: """Gets a string containing the spacing in CSS format. Returns: str: Spacing in CSS format. """ top, right, bottom, left = self if top == right == bottom == left: return f"{top}" if (top, right) == (bottom, left): return f"{top} {right}" else: return f"{top} {right} {bottom} {left}" @classmethod def unpack(cls, pad: SpacingDimensions) -> Spacing: """Unpack padding specified in CSS style. Args: pad (SpacingDimensions): An integer, or tuple of 1, 2, or 4 integers. Raises: ValueError: If `pad` is an invalid value. Returns: Spacing: New Spacing object. """ if isinstance(pad, int): return cls(pad, pad, pad, pad) pad_len = len(pad) if pad_len == 1: _pad = pad[0] return cls(_pad, _pad, _pad, _pad) if pad_len == 2: pad_top, pad_right = cast(Tuple[int, int], pad) return cls(pad_top, pad_right, pad_top, pad_right) if pad_len == 4: top, right, bottom, left = cast(Tuple[int, int, int, int], pad) return cls(top, right, bottom, left) raise ValueError( f"1, 2 or 4 integers required for spacing properties; {pad_len} given" ) @classmethod def vertical(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on vertical edges, and no horizontal spacing. Args: amount (int): The magnitude of spacing to apply to vertical edges Returns: Spacing: `Spacing(amount, 0, amount, 0)` """ return Spacing(amount, 0, amount, 0) @classmethod def horizontal(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on horizontal edges, and no vertical spacing. Args: amount (int): The magnitude of spacing to apply to horizontal edges Returns: Spacing: `Spacing(0, amount, 0, amount)` """ return Spacing(0, amount, 0, amount) @classmethod def all(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on all edges. Args: amount (int): The magnitude of spacing to apply to all edges Returns: Spacing: `Spacing(amount, amount, amount, amount)` """ return Spacing(amount, amount, amount, amount) def __add__(self, other: object) -> Spacing: if isinstance(other, tuple): top1, right1, bottom1, left1 = self top2, right2, bottom2, left2 = other return Spacing( top1 + top2, right1 + right2, bottom1 + bottom2, left1 + left2 ) return NotImplemented def __sub__(self, other: object) -> Spacing: if isinstance(other, tuple): top1, right1, bottom1, left1 = self top2, right2, bottom2, left2 = other return Spacing( top1 - top2, right1 - right2, bottom1 - bottom2, left1 - left2 ) return NotImplemented def grow_maximum(self, other: Spacing) -> Spacing: """Grow spacing with a maximum. Args: other (Spacing): Spacing object. Returns: Spacing: New spacing were the values are maximum of the two values. """ top, right, bottom, left = self other_top, other_right, other_bottom, other_left = other return Spacing( max(top, other_top), max(right, other_right), max(bottom, other_bottom), max(left, other_left), )
Ancestors
- builtins.tuple
Static methods
def all(amount: int) ‑> Spacing
-
Construct a Spacing with a given amount of spacing on all edges.
Args
amount
:int
- The magnitude of spacing to apply to all edges
Returns
Expand source code
@classmethod def all(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on all edges. Args: amount (int): The magnitude of spacing to apply to all edges Returns: Spacing: `Spacing(amount, amount, amount, amount)` """ return Spacing(amount, amount, amount, amount)
def horizontal(amount: int) ‑> Spacing
-
Construct a Spacing with a given amount of spacing on horizontal edges, and no vertical spacing.
Args
amount
:int
- The magnitude of spacing to apply to horizontal edges
Returns
Expand source code
@classmethod def horizontal(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on horizontal edges, and no vertical spacing. Args: amount (int): The magnitude of spacing to apply to horizontal edges Returns: Spacing: `Spacing(0, amount, 0, amount)` """ return Spacing(0, amount, 0, amount)
def unpack(pad: SpacingDimensions) ‑> Spacing
-
Unpack padding specified in CSS style.
Args
pad
:SpacingDimensions
- An integer, or tuple of 1, 2, or 4 integers.
Raises
ValueError
- If
pad
is an invalid value.
Returns
Spacing
- New Spacing object.
Expand source code
@classmethod def unpack(cls, pad: SpacingDimensions) -> Spacing: """Unpack padding specified in CSS style. Args: pad (SpacingDimensions): An integer, or tuple of 1, 2, or 4 integers. Raises: ValueError: If `pad` is an invalid value. Returns: Spacing: New Spacing object. """ if isinstance(pad, int): return cls(pad, pad, pad, pad) pad_len = len(pad) if pad_len == 1: _pad = pad[0] return cls(_pad, _pad, _pad, _pad) if pad_len == 2: pad_top, pad_right = cast(Tuple[int, int], pad) return cls(pad_top, pad_right, pad_top, pad_right) if pad_len == 4: top, right, bottom, left = cast(Tuple[int, int, int, int], pad) return cls(top, right, bottom, left) raise ValueError( f"1, 2 or 4 integers required for spacing properties; {pad_len} given" )
def vertical(amount: int) ‑> Spacing
-
Construct a Spacing with a given amount of spacing on vertical edges, and no horizontal spacing.
Args
amount
:int
- The magnitude of spacing to apply to vertical edges
Returns
Expand source code
@classmethod def vertical(cls, amount: int) -> Spacing: """Construct a Spacing with a given amount of spacing on vertical edges, and no horizontal spacing. Args: amount (int): The magnitude of spacing to apply to vertical edges Returns: Spacing: `Spacing(amount, 0, amount, 0)` """ return Spacing(amount, 0, amount, 0)
Instance variables
var bottom : int
-
Space from the bottom of a region.
var bottom_right : tuple[int, int]
-
Bottom right space.
Returns
tuple[int, int]
(<right>, <bottom>)
Expand source code
@property def bottom_right(self) -> tuple[int, int]: """Bottom right space. Returns: tuple[int, int]: `(<right>, <bottom>)` """ return (self.right, self.bottom)
var css : str
-
Gets a string containing the spacing in CSS format.
Returns
str
- Spacing in CSS format.
Expand source code
@property def css(self) -> str: """Gets a string containing the spacing in CSS format. Returns: str: Spacing in CSS format. """ top, right, bottom, left = self if top == right == bottom == left: return f"{top}" if (top, right) == (bottom, left): return f"{top} {right}" else: return f"{top} {right} {bottom} {left}"
var height : int
-
Total space in height.
Returns
int
- Height.
Expand source code
@property def height(self) -> int: """Total space in height. Returns: int: Height. """ return self.top + self.bottom
var left : int
-
Space from the left of a region.
var right : int
-
Space from the left of a region.
var top : int
-
Space from the top of a region.
var top_left : tuple[int, int]
-
Top left space.
Returns
tuple[int, int]
(<left>, <top>)
Expand source code
@property def top_left(self) -> tuple[int, int]: """Top left space. Returns: tuple[int, int]: `(<left>, <top>)` """ return (self.left, self.top)
var totals : tuple[int, int]
-
Get total horizontal and vertical space.
Returns
tuple[int, int]
(<horizontal>, <vertical>)
Expand source code
@property def totals(self) -> tuple[int, int]: """Get total horizontal and vertical space. Returns: tuple[int, int]: `(<horizontal>, <vertical>)` """ top, right, bottom, left = self return (left + right, top + bottom)
var width : int
-
Total space in width.
Returns
int
- Width.
Expand source code
@property def width(self) -> int: """Total space in width. Returns: int: Width. """ return self.left + self.right
Methods
def grow_maximum(self, other: Spacing) ‑> Spacing
-
Grow spacing with a maximum.
Args
other
:Spacing
- Spacing object.
Returns
Spacing
- New spacing were the values are maximum of the two values.
Expand source code
def grow_maximum(self, other: Spacing) -> Spacing: """Grow spacing with a maximum. Args: other (Spacing): Spacing object. Returns: Spacing: New spacing were the values are maximum of the two values. """ top, right, bottom, left = self other_top, other_right, other_bottom, other_left = other return Spacing( max(top, other_top), max(right, other_right), max(bottom, other_bottom), max(left, other_left), )