Skip to content

Geometry

Module: terminaltexteffects.utils.geometry

This module provides utility functions for geometric calculations and operations.

The purpose of these functions is to find terminal coordinates that fall within certain regions or along certain paths. These functions are used by effects to enable more complex animations and movement paths.

Functions:

Name Description
find_coords_on_circle

Finds points on a circle given the origin, radius, and number of points.

find_coords_in_circle

Finds coordinates within an ellipse given the center and major axis length.

find_coords_in_rect

Finds coordinates within a rectangle given the origin and distance.

find_coord_at_distance

Finds the coordinate at a given distance along a line defined by two coordinates.

find_coord_on_bezier_curve

Finds points on a quadratic or cubic bezier curve.

find_coord_on_line

Finds points on a line.

find_length_of_bezier_curve

Finds the length of a quadratic or cubic bezier curve.

find_length_of_line

Finds the length of a line intersecting two coordinates.

find_normalized_distance_from_center

Returns the normalized distance from the center of the Canvas.

Coord dataclass

A coordinate with row and column values.

Parameters:

Name Type Description Default
column int

column value

required
row int

row value

required
Source code in terminaltexteffects/utils/geometry.py
@dataclass(eq=True, frozen=True)
class Coord:
    """A coordinate with row and column values.

    Args:
        column (int): column value
        row (int): row value"""

    column: int
    row: int

find_coord_at_distance(origin, target, distance)

Finds the coordinate at the given distance along the line defined by the origin and target coordinates.

Parameters:

Name Type Description Default
origin Coord

origin coordinate (a)

required
target Coord

target coordinate (b)

required
distance float

distance from the target coordinate (b), away from the origin coordinate (a)

required

Returns:

Name Type Description
Coord Coord

Coordinate at the given distance (c).

Source code in terminaltexteffects/utils/geometry.py
def find_coord_at_distance(origin: Coord, target: Coord, distance: float) -> Coord:
    """Finds the coordinate at the given distance along the line defined by the origin and target coordinates.

    Args:
        origin (Coord): origin coordinate (a)
        target (Coord): target coordinate (b)
        distance (float): distance from the target coordinate (b), away from the origin coordinate (a)

    Returns:
        Coord: Coordinate at the given distance (c).
    """
    total_distance = find_length_of_line(origin, target) + distance
    if total_distance == 0 or origin == target:
        return origin
    t = total_distance / find_length_of_line(origin, target)
    next_column, next_row = (
        ((1 - t) * origin.column + t * target.column),
        ((1 - t) * origin.row + t * target.row),
    )
    return Coord(round(next_column), round(next_row))

find_coord_on_bezier_curve(start, control, end, t)

Finds points on a quadratic or cubic bezier curve.

Parameters:

Name Type Description Default
start Coord

The starting coordinate of the curve.

required
control tuple[Coord, ...] | Coord

The control point(s) of the curve. For a quadratic bezier curve, a single control point is expected. For a cubic bezier curve, two control points are expected.

required
end Coord

The ending coordinate of the curve.

required
t float

The parameter value between 0 and 1 that determines the position on the curve.

required

Returns:

Name Type Description
Coord Coord

The coordinate on the bezier curve corresponding to the given parameter value.

Source code in terminaltexteffects/utils/geometry.py
def find_coord_on_bezier_curve(start: Coord, control: tuple[Coord, ...] | Coord, end: Coord, t: float) -> Coord:
    """
    Finds points on a quadratic or cubic bezier curve.

    Args:
        start (Coord): The starting coordinate of the curve.
        control (tuple[Coord, ...] | Coord): The control point(s) of the curve.
            For a quadratic bezier curve, a single control point is expected.
            For a cubic bezier curve, two control points are expected.
        end (Coord): The ending coordinate of the curve.
        t (float): The parameter value between 0 and 1 that determines the position on the curve.

    Returns:
        Coord: The coordinate on the bezier curve corresponding to the given parameter value.
    """
    if isinstance(control, Coord):
        control = (control,)
    if len(control) == 1:
        control1 = control[0]
        x = (1 - t) ** 2 * start.column + 2 * (1 - t) * t * control1.column + t**2 * end.column
        y = (1 - t) ** 2 * start.row + 2 * (1 - t) * t * control1.row + t**2 * end.row
    elif len(control) == 2:
        control1, control2 = control
        x = (
            (1 - t) ** 3 * start.column
            + 3 * (1 - t) ** 2 * t * control1.column
            + 3 * (1 - t) * t**2 * control2.column
            + t**3 * end.column
        )
        y = (
            (1 - t) ** 3 * start.row
            + 3 * (1 - t) ** 2 * t * control1.row
            + 3 * (1 - t) * t**2 * control2.row
            + t**3 * end.row
        )
    return Coord(round(x), round(y))

find_coord_on_line(start, end, t)

Finds points on a line.

Parameters:

Name Type Description Default
start Coord

The starting coordinate of the line.

required
end Coord

The ending coordinate of the line.

required
t float

The parameter value between 0 and 1 representing the position on the line.

required

Returns:

Name Type Description
Coord Coord

The coordinate on the line corresponding to the given parameter value.

Source code in terminaltexteffects/utils/geometry.py
def find_coord_on_line(start: Coord, end: Coord, t: float) -> Coord:
    """
    Finds points on a line.

    Args:
        start (Coord): The starting coordinate of the line.
        end (Coord): The ending coordinate of the line.
        t (float): The parameter value between 0 and 1 representing the position on the line.

    Returns:
        Coord: The coordinate on the line corresponding to the given parameter value.
    """
    x = (1 - t) * start.column + t * end.column
    y = (1 - t) * start.row + t * end.row
    return Coord(round(x), round(y))

find_coords_in_circle(center, diameter)

Find the coordinates within an circle given the center and diameter. The actual shape calculated is an ellipse with a major axis of length diameter, however the terminal cell height/width ratio creates a circle visually.

Parameters:

Name Type Description Default
center Coord

The center coordinate of the circle.

required
diameter int

The length of the major axis of the circle.

required

Returns:

Type Description
list[Coord]

list[Coord]: A list of coordinates within the circle.

Source code in terminaltexteffects/utils/geometry.py
def find_coords_in_circle(center: Coord, diameter: int) -> list[Coord]:
    """
    Find the coordinates within an circle given the center and diameter. The actual
    shape calculated is an ellipse with a major axis of length diameter, however the
    terminal cell height/width ratio creates a circle visually.

    Args:
        center (Coord): The center coordinate of the circle.
        diameter (int): The length of the major axis of the circle.

    Returns:
        list[Coord]: A list of coordinates within the circle.
    """

    h, k = center.column, center.row
    coords_in_ellipse: list[Coord] = []
    a_squared = diameter**2
    b_squared = (diameter / 2) ** 2

    for x in range(h - diameter, h + diameter + 1):
        x_component = ((x - h) ** 2) / a_squared
        max_y_offset = int((b_squared * (1 - x_component)) ** 0.5)
        for y in range(k - max_y_offset, k + max_y_offset + 1):
            coords_in_ellipse.append(Coord(x, y))

    return coords_in_ellipse

find_coords_in_rect(origin, distance)

Find coords that fall within a rectangle with the given origin and distance from the origin. Distance specifies the number of units in each direction from the origin. Final width = 2 * distance + 1, final height = 2 * distance + 1.

Parameters:

Name Type Description Default
origin Coord

center of the rectangle

required
distance int

distance from the origin

required

Returns:

Type Description
list[Coord]

list[Coord]: list of Coord points in the rectangle

Source code in terminaltexteffects/utils/geometry.py
def find_coords_in_rect(origin: Coord, distance: int) -> list[Coord]:
    """Find coords that fall within a rectangle with the given origin and distance
    from the origin. Distance specifies the number of units in each direction from the origin.
    Final width = 2 * distance + 1, final height = 2 * distance + 1.

    Args:
        origin (Coord): center of the rectangle
        distance (int): distance from the origin

    Returns:
        list[Coord]: list of Coord points in the rectangle
    """
    left_boundary = origin.column - distance
    right_boundary = origin.column + distance
    top_boundary = origin.row - distance
    bottom_boundary = origin.row + distance
    coords: list[Coord] = []
    for column in range(left_boundary, right_boundary + 1):
        for row in range(top_boundary, bottom_boundary + 1):
            coords.append(Coord(column, row))

    return coords

find_coords_on_circle(origin, radius, coords_limit=0, unique=True)

Finds points on a circle.

Parameters:

Name Type Description Default
origin Coord

origin of the circle

required
radius int

radius of the circle

required
coords_limit int

limit the number of coords returned, if 0, the number of points is calculated based on the circumference of the circle

0
unique bool

whether to remove duplicate points. Defaults to True.

True

Returns:

Name Type Description
list Coord

list of Coord points on the circle

Source code in terminaltexteffects/utils/geometry.py
def find_coords_on_circle(origin: Coord, radius: int, coords_limit: int = 0, unique: bool = True) -> list[Coord]:
    """Finds points on a circle.

    Args:
        origin (Coord): origin of the circle
        radius (int): radius of the circle
        coords_limit (int): limit the number of coords returned, if 0, the number of points is calculated based on the circumference of the circle
        unique (bool): whether to remove duplicate points. Defaults to True.

    Returns:
        list (Coord): list of Coord points on the circle
    """
    points = []
    seen_points = set()
    if not coords_limit:
        coords_limit = round(2 * math.pi * radius)
    angle_step = 2 * math.pi / coords_limit
    for i in range(coords_limit):
        angle = angle_step * i
        x = origin.column + radius * math.cos(angle)
        # correct for terminal character height/width ratio by doubling the x distance from origin
        x_diff = x - origin.column
        x += x_diff
        y = origin.row + radius * math.sin(angle)
        point_coord = Coord(round(x), round(y))
        if unique:
            if point_coord not in seen_points:
                points.append(point_coord)
        else:
            points.append(point_coord)
        seen_points.add(point_coord)

    return points

find_length_of_bezier_curve(start, control, end)

Finds the length of a quadratic or cubic bezier curve.

Parameters:

Name Type Description Default
start Coord

The starting coordinate of the curve.

required
control tuple[Coord, ...] | Coord

The control point(s) of the curve.

required
end Coord

The ending coordinate of the curve.

required

Returns:

Name Type Description
float float

The length of the bezier curve.

Source code in terminaltexteffects/utils/geometry.py
def find_length_of_bezier_curve(start: Coord, control: tuple[Coord, ...] | Coord, end: Coord) -> float:
    """
    Finds the length of a quadratic or cubic bezier curve.

    Args:
        start (Coord): The starting coordinate of the curve.
        control (tuple[Coord, ...] | Coord): The control point(s) of the curve.
        end (Coord): The ending coordinate of the curve.

    Returns:
        float: The length of the bezier curve.
    """
    length = 0.0
    prev_coord = start
    for t in range(1, 10):
        coord = find_coord_on_bezier_curve(start, control, end, t / 10)
        length += find_length_of_line(prev_coord, coord)
        prev_coord = coord
        prev_coord = coord
    return length

find_length_of_line(coord1, coord2, double_row_diff=False)

Returns the length of the line intersecting coord1 and coord2. If double_row_diff is True, the distance is doubled to account for the terminal character height/width ratio.

Parameters:

Name Type Description Default
coord1 Coord

first coordinate.

required
coord2 Coord

second coordinate.

required
double_row_diff bool

whether to double the row difference to account for terminal character height/width ratio. Defaults to False.

False

Returns:

Name Type Description
float float

length of the line

Source code in terminaltexteffects/utils/geometry.py
def find_length_of_line(coord1: Coord, coord2: Coord, double_row_diff: bool = False) -> float:
    """Returns the length of the line intersecting coord1 and coord2. If double_row_diff is True, the distance is
    doubled to account for the terminal character height/width ratio.

    Args:
        coord1 (Coord): first coordinate.
        coord2 (Coord): second coordinate.
        double_row_diff (bool, optional): whether to double the row difference to account for terminal character height/width ratio. Defaults to False.

    Returns:
        float: length of the line
    """
    column_diff = coord2.column - coord1.column
    row_diff = coord2.row - coord1.row
    if double_row_diff:
        return math.hypot(column_diff, 2 * row_diff)
    return math.hypot(column_diff, row_diff)

find_normalized_distance_from_center(max_row, max_column, other_coord)

Returns the normalized distance from the center of the Canvas as a float between 0 and 1.

The distance is calculated using the Pythagorean theorem and accounts for the aspect ratio of the terminal.

Parameters:

Name Type Description Default
max_row int

Maximum row value of the Canvas.

required
max_column int

Maximum column value of the Canvas.

required
other_coord Coord

Other coordinate from which to calculate the distance.

required

Returns:

Name Type Description
float float

Normalized distance from the center of the Canvas, float between 0 and 1.

Source code in terminaltexteffects/utils/geometry.py
def find_normalized_distance_from_center(max_row: int, max_column: int, other_coord: Coord) -> float:
    """Returns the normalized distance from the center of the Canvas as a float between 0 and 1.

    The distance is calculated using the Pythagorean theorem and accounts for the aspect ratio of the terminal.

    Args:
        max_row (int): Maximum row value of the Canvas.
        max_column (int): Maximum column value of the Canvas.
        other_coord (Coord): Other coordinate from which to calculate the distance.

    Returns:
        float: Normalized distance from the center of the Canvas, float between 0 and 1.
    """
    center_x = max_column / 2
    center_y = max_row / 2

    max_distance = ((max_column**2) + ((max_row * 2) ** 2)) ** 0.5

    distance = ((other_coord.column - center_x) ** 2 + (((other_coord.row) - center_y) * 2) ** 2) ** 0.5

    return distance / (max_distance / 2)