Skip to content

Gradient

Module: terminaltexteffects.utils.graphics

Basic Usage

from terminaltexteffects.utils.graphics import Color, Gradient

rgb = Gradient(Color("#ff0000"), Color("#00ff00"), Color("#0000ff"), steps=5)
for color in rgb:
    assert isinstance(color, Color)
    print(color.rgb_color)

Iteration and indexing return Color objects. Use color.rgb_color when a six-digit RGB string is needed.

Stops, Steps, and Loops

Provide one or more Color stops. A step count is the number of transitions between two adjacent stops, so a two-stop gradient with steps=4 contains five colors, including both stops:

from terminaltexteffects.utils.graphics import Color, Gradient

gradient = Gradient(Color("000000"), Color("ffffff"), steps=4)
assert [color.rgb_color for color in gradient] == [
    "000000",
    "404040",
    "808080",
    "bfbfbf",
    "ffffff",
]

A single integer applies to every transition. A tuple assigns counts in stop-pair order; when it is shorter than the number of transitions, its final value is repeated. A tuple cannot be empty or longer than the number of transitions, and every count must be a positive, non-boolean integer. A single-stop gradient always contains that one stop after validating its step values.

With loop=True, a gradient with multiple stops adds a final transition from the last stop back to the first. The first stop therefore also appears as the final spectrum color:

loop = Gradient(Color("ffffff"), Color("000000"), steps=(2, 3), loop=True)
assert [color.rgb_color for color in loop] == [
    "ffffff",
    "808080",
    "000000",
    "555555",
    "aaaaaa",
    "ffffff",
]

Invalid stops raise TypeError. Missing stops, invalid step counts, and overlong step tuples raise ValueError.

Fractional Lookup

get_color_at_fraction() accepts a finite number from 0 through 1 and selects the nearest precomputed spectrum sample. Half-sample ties use Python's ties-to-even round() behavior. Non-numeric values raise TypeError; non-finite or out-of-range values raise ValueError.

Coordinate Mappings

Coordinate mappings include every row and column bound and return dict[Coord, Color]:

from terminaltexteffects.utils.geometry import Coord
from terminaltexteffects.utils.graphics import Color, Gradient

gradient = Gradient(Color("000000"), Color("ffffff"), steps=2)
mapping = gradient.build_coordinate_color_mapping(
    min_row=1,
    max_row=1,
    min_column=4,
    max_column=6,
    direction=Gradient.Direction.HORIZONTAL,
)

assert [mapping[Coord(column, 1)].rgb_color for column in range(4, 7)] == [
    "000000",
    "808080",
    "ffffff",
]
  • HORIZONTAL maps the first and final colors to min_column and max_column.
  • VERTICAL maps the first and final colors to min_row and max_row.
  • DIAGONAL maps the first color at (min_column, min_row) and the final color at (max_column, max_row).
  • RADIAL measures from the rectangle's geometric center toward its corners; the center is fraction 0 and every corner is fraction 1.

A direction with no span uses the first color. Bounds must be non-boolean integers greater than zero, each minimum must not exceed its maximum, and direction must be a Gradient.Direction. Invalid bound types or directions raise TypeError; invalid bound values raise ValueError.

Printing Gradients

Gradients can be printed to the terminal to show information about the stops, steps, and resulting spectrum.

Printed Gradient showing its stops, steps, and spectrum


Gradient Reference

A Gradient is a list of Color objects transitioning from one color stop to another.

The spectrum is calculated using linear interpolation between adjacent stops. Iteration and indexing return Color objects. If there is only one stop, the gradient contains that color once because there are no transitions.

Ex: stops = ("ffffff", "aaaaaa", "000000"), steps = (6, 3)

"ffffff" -> (6 transitions) -> "aaaaaa" -> (3 transitions) -> "000000"

A step count is the number of transitions between a pair of adjacent stops. Total number of colors in a multi-stop gradient spectrum is the sum of the effective step counts plus 1. A single integer applies to every transition. A tuple assigns counts in order; if it contains fewer values than transitions, its last value is repeated. When transitions exist, a tuple cannot contain more values than there are transitions. For a single-stop gradient, step values are validated but tuple cardinality does not change the one-color spectrum.

With loop=True and multiple stops, a closing transition is generated from the last stop back to the first, and the first stop is repeated as the final spectrum color.

Attributes:

Name Type Description
spectrum list[Color]

The generated Color objects. Multi-stop gradients contain sum(effective_steps) + 1 colors; single-stop gradients contain one color.

Source code in terminaltexteffects/utils/graphics.py
class Gradient:
    """A Gradient is a list of `Color` objects transitioning from one color stop to another.

    The spectrum is calculated using linear interpolation between adjacent stops. Iteration and indexing return
    `Color` objects. If there is only one stop, the gradient contains that color once because there are no transitions.

    Ex: stops = ("ffffff", "aaaaaa", "000000"), steps = (6, 3)

    "ffffff" -> (6 transitions) -> "aaaaaa" -> (3 transitions) -> "000000"

    A step count is the number of transitions between a pair of adjacent stops. Total number of colors in a
    multi-stop gradient spectrum is the sum of the effective step counts plus 1. A single integer applies to every
    transition. A tuple assigns counts in order; if it contains fewer values than transitions, its last value is
    repeated. When transitions exist, a tuple cannot contain more values than there are transitions. For a single-stop
    gradient, step values are validated but tuple cardinality does not change the one-color spectrum.

    With `loop=True` and multiple stops, a closing transition is generated from the last stop back to the first, and
    the first stop is repeated as the final spectrum color.

    Attributes:
        spectrum (list[Color]): The generated `Color` objects. Multi-stop gradients contain
            `sum(effective_steps) + 1` colors; single-stop gradients contain one color.

    """

    class Direction(Enum):
        """Enum for specifying the direction of the gradient."""

        VERTICAL = auto()
        HORIZONTAL = auto()
        RADIAL = auto()
        DIAGONAL = auto()

    def __init__(self, *stops: Color, steps: tuple[int, ...] | int = 1, loop: bool = False) -> None:
        """Initialize a Gradient object.

        Args:
            stops (Color): One or more `Color` objects representing the color stops.
            steps (int | tuple[int, ...], optional): Number of transitions or a tuple of transition counts for
                generating the spectrum. A single value is repeated for every adjacent stop pair. Defaults to 1.
            loop (bool, optional): Add a closing transition from the final stop back to the first when multiple stops
                are present. The first stop is repeated as the final spectrum color. Defaults to False.

        Raises:
            TypeError: If any stop is not a `Color`.
            ValueError: If no color stops are provided, a step count is invalid, or a steps tuple has more entries
                than the gradient has transitions.

        Attributes:
            _stops (tuple[Color]): Tuple of Color objects representing the color stops.
            _steps (int | tuple[int, ...]): Transition count or tuple of transition counts used to generate the
                spectrum.
            _loop (bool): Whether to generate a closing transition back to the first stop.
            spectrum (list[Color]): List of generated `Color` objects representing the spectrum.

        Returns:
            None

        """
        self._stops = stops
        if len(self._stops) < 1:
            msg = "At least one stop must be provided."
            raise ValueError(msg)
        if any(not isinstance(stop, Color) for stop in self._stops):
            msg = "Stops must be Color instances."
            raise TypeError(msg)
        step_values = self._validate_steps(steps)
        self._steps = steps
        self._loop = loop
        transition_count = len(self._stops) if self._loop and len(self._stops) > 1 else len(self._stops) - 1
        if transition_count and len(step_values) > transition_count:
            msg = "A steps tuple cannot contain more values than the gradient has transitions."
            raise ValueError(msg)
        effective_steps = step_values + (step_values[-1],) * (transition_count - len(step_values))
        self.spectrum: list[Color] = self._generate(effective_steps)

    @staticmethod
    def _validate_steps(steps: int | tuple[int, ...]) -> tuple[int, ...]:
        """Validate and return gradient transition counts as a tuple."""
        step_values = (steps,) if isinstance(steps, int) and not isinstance(steps, bool) else steps
        if not isinstance(step_values, tuple) or not step_values:
            msg = "Steps must be a positive integer or a non-empty tuple of positive integers."
            raise ValueError(msg)
        if any(isinstance(step, bool) or not isinstance(step, int) or step < 1 for step in step_values):
            msg = "Steps must be a positive integer or a non-empty tuple of positive integers."
            raise ValueError(msg)
        return step_values

    def get_color_at_fraction(self, fraction: float) -> Color:
        """Return the precomputed spectrum color corresponding to a normalized fraction.

        The fraction is matched to the nearest precomputed spectrum color from start
        to end. Ties use Python's round-to-even behavior. A fraction of `0` returns
        the first color and `1` returns the final color.

        Args:
            fraction (float): The fraction of the gradient to get the color for.

        Returns:
            Color: The color at the fraction of the gradient.

        Raises:
            TypeError: If `fraction` is not an integer or float.
            ValueError: If `fraction` is not finite or outside the inclusive range from `0` to `1`.

        """
        if isinstance(fraction, bool) or not isinstance(fraction, (int, float)):
            msg = "Fraction must be an integer or float."
            raise TypeError(msg)
        if not math.isfinite(fraction):
            msg = "Fraction must be finite."
            raise ValueError(msg)
        if fraction < 0 or fraction > 1:
            msg = "Fraction must be 0 <= fraction <= 1."
            raise ValueError(msg)
        spectrum_index = round(fraction * (len(self.spectrum) - 1))
        return self.spectrum[spectrum_index]

    def _generate(self, steps: tuple[int, ...]) -> list[Color]:
        """Generate the spectrum by interpolating between adjacent stops.

        If there is only one stop, the spectrum contains that color once. Otherwise, each value in `steps` is the
        number of transitions for its corresponding adjacent stop pair. Looping gradients include the closing pair
        from the final stop back to the first.

        For example, stops `("ffffff", "aaaaaa", "000000")` with effective transition counts `(6, 3)` produce ten
        colors: seven across the first pair and four across the second, with the shared `"aaaaaa"` stop included once.
        In general, a multi-stop spectrum contains `sum(steps) + 1` colors.

        Returns:
            list[Color]: Generated colors. The first and last colors are the start and end stops, respectively.

        """
        if len(self._stops) == 1:
            return [self._stops[0]]
        generation_stops = (*self._stops, self._stops[0]) if self._loop else self._stops
        spectrum = [generation_stops[0]]
        color_pairs = zip(generation_stops, generation_stops[1:])
        for (start, end), step_count in zip(color_pairs, steps):
            start_color_ints = start.rgb_ints
            end_color_ints = end.rgb_ints
            for i in range(1, step_count):
                fraction = i / step_count
                spectrum.append(Color._from_rgb_ints(_interpolate_rgb(start_color_ints, end_color_ints, fraction)))
            spectrum.append(end)
        return spectrum

    def build_coordinate_color_mapping(
        self,
        min_row: int,
        max_row: int,
        min_column: int,
        max_column: int,
        direction: Gradient.Direction,
    ) -> dict[geometry.Coord, Color]:
        """Build a mapping of coordinates to colors based on the gradient and a direction.

        For example, a vertical gradient will have the same color for each character in a row. When applied across all
        characters in the canvas, the gradient will be visible as a vertical gradient. The mapping respects the
        provided row and column bounds for every direction. Vertical, horizontal, and diagonal mappings assign the
        first and final gradient colors to the endpoints of each non-degenerate directional span. Radial mappings
        assign the first color at the center and the final color at the corners. A mapping containing only one
        coordinate receives the first color in every direction.

        Args:
            min_row (int): The minimum row value. Must be greater than 0 and less than or equal to max_row.
            max_row (int): The maximum row value. Must be greater than 0 and greater than or equal to min_row.
            min_column (int): The minimum column value. Must be greater than 0 and less than or equal to max_column.
            max_column (int): The maximum column value. Must be greater than 0 and greater than or equal to min_column.
            direction (Gradient.Direction): The direction of the gradient.

        Returns:
            dict[geometry.Coord, Color]: A mapping of coordinates to `Color` objects.

        Raises:
            TypeError: If `direction` is not a `Gradient.Direction` or any coordinate bound is not a non-boolean
                integer.
            ValueError: If any coordinate bound is less than one or a minimum bound exceeds its maximum.

        """
        if not isinstance(direction, Gradient.Direction):
            msg = "direction must be a Gradient.Direction."
            raise TypeError(msg)
        bounds = (min_row, max_row, min_column, max_column)
        if any(isinstance(value, bool) or not isinstance(value, int) for value in bounds):
            msg = "Coordinate bounds must be non-boolean integers."
            raise TypeError(msg)
        if any(value < 1 for value in bounds):
            msg = "Coordinate bounds must be greater than 0."
            raise ValueError(msg)
        if min_row > max_row or min_column > max_column:
            msg = "min_row and min_column must be less than or equal to max_row and max_column."
            raise ValueError(msg)
        row_span = max_row - min_row
        column_span = max_column - min_column
        gradient_mapping: dict[geometry.Coord, Color] = {}
        if direction == Gradient.Direction.VERTICAL:
            for row_value in range(min_row, max_row + 1):
                fraction = (row_value - min_row) / row_span if row_span else 0
                color = self.get_color_at_fraction(fraction)
                for column_value in range(min_column, max_column + 1):
                    gradient_mapping[geometry.Coord(column_value, row_value)] = color
        elif direction == Gradient.Direction.HORIZONTAL:
            for column_value in range(min_column, max_column + 1):
                fraction = (column_value - min_column) / column_span if column_span else 0
                color = self.get_color_at_fraction(fraction)
                for row_value in range(min_row, max_row + 1):
                    gradient_mapping[geometry.Coord(column_value, row_value)] = color
        elif direction == Gradient.Direction.RADIAL:
            for row_value in range(min_row, max_row + 1):
                for column_value in range(min_column, max_column + 1):
                    distance_from_center = geometry.find_normalized_distance_from_center(
                        min_row,
                        max_row,
                        min_column,
                        max_column,
                        geometry.Coord(column_value, row_value),
                    )
                    color = self.get_color_at_fraction(distance_from_center)
                    gradient_mapping[geometry.Coord(column_value, row_value)] = color
        elif direction == Gradient.Direction.DIAGONAL:
            diagonal_span = (row_span * geometry.TERMINAL_ROW_SCALE) + column_span
            for row_value in range(min_row, max_row + 1):
                for column_value in range(min_column, max_column + 1):
                    fraction = ((row_value - min_row) * geometry.TERMINAL_ROW_SCALE) + (column_value - min_column)
                    fraction = fraction / diagonal_span if diagonal_span else 0
                    color = self.get_color_at_fraction(fraction)
                    gradient_mapping[geometry.Coord(column_value, row_value)] = color

        return gradient_mapping

    def __iter__(self) -> Iterator[Color]:
        """Return an iterator over the Gradient object."""
        yield from self.spectrum

    def __len__(self) -> int:
        """Return the length of the Gradient object."""
        return len(self.spectrum)

    @typing.overload
    def __getitem__(self, index: int) -> Color: ...

    @typing.overload
    def __getitem__(self, index: slice) -> list[Color]: ...

    def __getitem__(self, index: int | slice) -> Color | list[Color]:
        """Return the color at the given index or a list of colors based on the slice."""
        return self.spectrum[index]

    def __str__(self) -> str:
        """Return a string representation of the Gradient object."""
        color_blocks = [f"{colorterm.fg(color.rgb_color)}█{ansitools.reset_all()}" for color in self.spectrum]
        return f"Gradient: Stops({', '.join(c.rgb_color for c in self._stops)}), Steps({self._steps})\n" + "".join(
            color_blocks,
        )

Direction

Bases: Enum

Enum for specifying the direction of the gradient.

Source code in terminaltexteffects/utils/graphics.py
class Direction(Enum):
    """Enum for specifying the direction of the gradient."""

    VERTICAL = auto()
    HORIZONTAL = auto()
    RADIAL = auto()
    DIAGONAL = auto()

__getitem__(index)

__getitem__(index: int) -> Color
__getitem__(index: slice) -> list[Color]

Return the color at the given index or a list of colors based on the slice.

Source code in terminaltexteffects/utils/graphics.py
def __getitem__(self, index: int | slice) -> Color | list[Color]:
    """Return the color at the given index or a list of colors based on the slice."""
    return self.spectrum[index]

__init__(*stops, steps=1, loop=False)

Initialize a Gradient object.

Parameters:

Name Type Description Default
stops Color

One or more Color objects representing the color stops.

()
steps int | tuple[int, ...]

Number of transitions or a tuple of transition counts for generating the spectrum. A single value is repeated for every adjacent stop pair. Defaults to 1.

1
loop bool

Add a closing transition from the final stop back to the first when multiple stops are present. The first stop is repeated as the final spectrum color. Defaults to False.

False

Raises:

Type Description
TypeError

If any stop is not a Color.

ValueError

If no color stops are provided, a step count is invalid, or a steps tuple has more entries than the gradient has transitions.

Attributes:

Name Type Description
_stops tuple[Color]

Tuple of Color objects representing the color stops.

_steps int | tuple[int, ...]

Transition count or tuple of transition counts used to generate the spectrum.

_loop bool

Whether to generate a closing transition back to the first stop.

spectrum list[Color]

List of generated Color objects representing the spectrum.

Returns:

Type Description
None

None

Source code in terminaltexteffects/utils/graphics.py
def __init__(self, *stops: Color, steps: tuple[int, ...] | int = 1, loop: bool = False) -> None:
    """Initialize a Gradient object.

    Args:
        stops (Color): One or more `Color` objects representing the color stops.
        steps (int | tuple[int, ...], optional): Number of transitions or a tuple of transition counts for
            generating the spectrum. A single value is repeated for every adjacent stop pair. Defaults to 1.
        loop (bool, optional): Add a closing transition from the final stop back to the first when multiple stops
            are present. The first stop is repeated as the final spectrum color. Defaults to False.

    Raises:
        TypeError: If any stop is not a `Color`.
        ValueError: If no color stops are provided, a step count is invalid, or a steps tuple has more entries
            than the gradient has transitions.

    Attributes:
        _stops (tuple[Color]): Tuple of Color objects representing the color stops.
        _steps (int | tuple[int, ...]): Transition count or tuple of transition counts used to generate the
            spectrum.
        _loop (bool): Whether to generate a closing transition back to the first stop.
        spectrum (list[Color]): List of generated `Color` objects representing the spectrum.

    Returns:
        None

    """
    self._stops = stops
    if len(self._stops) < 1:
        msg = "At least one stop must be provided."
        raise ValueError(msg)
    if any(not isinstance(stop, Color) for stop in self._stops):
        msg = "Stops must be Color instances."
        raise TypeError(msg)
    step_values = self._validate_steps(steps)
    self._steps = steps
    self._loop = loop
    transition_count = len(self._stops) if self._loop and len(self._stops) > 1 else len(self._stops) - 1
    if transition_count and len(step_values) > transition_count:
        msg = "A steps tuple cannot contain more values than the gradient has transitions."
        raise ValueError(msg)
    effective_steps = step_values + (step_values[-1],) * (transition_count - len(step_values))
    self.spectrum: list[Color] = self._generate(effective_steps)

__iter__()

Return an iterator over the Gradient object.

Source code in terminaltexteffects/utils/graphics.py
def __iter__(self) -> Iterator[Color]:
    """Return an iterator over the Gradient object."""
    yield from self.spectrum

__len__()

Return the length of the Gradient object.

Source code in terminaltexteffects/utils/graphics.py
def __len__(self) -> int:
    """Return the length of the Gradient object."""
    return len(self.spectrum)

__str__()

Return a string representation of the Gradient object.

Source code in terminaltexteffects/utils/graphics.py
def __str__(self) -> str:
    """Return a string representation of the Gradient object."""
    color_blocks = [f"{colorterm.fg(color.rgb_color)}█{ansitools.reset_all()}" for color in self.spectrum]
    return f"Gradient: Stops({', '.join(c.rgb_color for c in self._stops)}), Steps({self._steps})\n" + "".join(
        color_blocks,
    )

build_coordinate_color_mapping(min_row, max_row, min_column, max_column, direction)

Build a mapping of coordinates to colors based on the gradient and a direction.

For example, a vertical gradient will have the same color for each character in a row. When applied across all characters in the canvas, the gradient will be visible as a vertical gradient. The mapping respects the provided row and column bounds for every direction. Vertical, horizontal, and diagonal mappings assign the first and final gradient colors to the endpoints of each non-degenerate directional span. Radial mappings assign the first color at the center and the final color at the corners. A mapping containing only one coordinate receives the first color in every direction.

Parameters:

Name Type Description Default
min_row int

The minimum row value. Must be greater than 0 and less than or equal to max_row.

required
max_row int

The maximum row value. Must be greater than 0 and greater than or equal to min_row.

required
min_column int

The minimum column value. Must be greater than 0 and less than or equal to max_column.

required
max_column int

The maximum column value. Must be greater than 0 and greater than or equal to min_column.

required
direction Direction

The direction of the gradient.

required

Returns:

Type Description
dict[Coord, Color]

dict[geometry.Coord, Color]: A mapping of coordinates to Color objects.

Raises:

Type Description
TypeError

If direction is not a Gradient.Direction or any coordinate bound is not a non-boolean integer.

ValueError

If any coordinate bound is less than one or a minimum bound exceeds its maximum.

Source code in terminaltexteffects/utils/graphics.py
def build_coordinate_color_mapping(
    self,
    min_row: int,
    max_row: int,
    min_column: int,
    max_column: int,
    direction: Gradient.Direction,
) -> dict[geometry.Coord, Color]:
    """Build a mapping of coordinates to colors based on the gradient and a direction.

    For example, a vertical gradient will have the same color for each character in a row. When applied across all
    characters in the canvas, the gradient will be visible as a vertical gradient. The mapping respects the
    provided row and column bounds for every direction. Vertical, horizontal, and diagonal mappings assign the
    first and final gradient colors to the endpoints of each non-degenerate directional span. Radial mappings
    assign the first color at the center and the final color at the corners. A mapping containing only one
    coordinate receives the first color in every direction.

    Args:
        min_row (int): The minimum row value. Must be greater than 0 and less than or equal to max_row.
        max_row (int): The maximum row value. Must be greater than 0 and greater than or equal to min_row.
        min_column (int): The minimum column value. Must be greater than 0 and less than or equal to max_column.
        max_column (int): The maximum column value. Must be greater than 0 and greater than or equal to min_column.
        direction (Gradient.Direction): The direction of the gradient.

    Returns:
        dict[geometry.Coord, Color]: A mapping of coordinates to `Color` objects.

    Raises:
        TypeError: If `direction` is not a `Gradient.Direction` or any coordinate bound is not a non-boolean
            integer.
        ValueError: If any coordinate bound is less than one or a minimum bound exceeds its maximum.

    """
    if not isinstance(direction, Gradient.Direction):
        msg = "direction must be a Gradient.Direction."
        raise TypeError(msg)
    bounds = (min_row, max_row, min_column, max_column)
    if any(isinstance(value, bool) or not isinstance(value, int) for value in bounds):
        msg = "Coordinate bounds must be non-boolean integers."
        raise TypeError(msg)
    if any(value < 1 for value in bounds):
        msg = "Coordinate bounds must be greater than 0."
        raise ValueError(msg)
    if min_row > max_row or min_column > max_column:
        msg = "min_row and min_column must be less than or equal to max_row and max_column."
        raise ValueError(msg)
    row_span = max_row - min_row
    column_span = max_column - min_column
    gradient_mapping: dict[geometry.Coord, Color] = {}
    if direction == Gradient.Direction.VERTICAL:
        for row_value in range(min_row, max_row + 1):
            fraction = (row_value - min_row) / row_span if row_span else 0
            color = self.get_color_at_fraction(fraction)
            for column_value in range(min_column, max_column + 1):
                gradient_mapping[geometry.Coord(column_value, row_value)] = color
    elif direction == Gradient.Direction.HORIZONTAL:
        for column_value in range(min_column, max_column + 1):
            fraction = (column_value - min_column) / column_span if column_span else 0
            color = self.get_color_at_fraction(fraction)
            for row_value in range(min_row, max_row + 1):
                gradient_mapping[geometry.Coord(column_value, row_value)] = color
    elif direction == Gradient.Direction.RADIAL:
        for row_value in range(min_row, max_row + 1):
            for column_value in range(min_column, max_column + 1):
                distance_from_center = geometry.find_normalized_distance_from_center(
                    min_row,
                    max_row,
                    min_column,
                    max_column,
                    geometry.Coord(column_value, row_value),
                )
                color = self.get_color_at_fraction(distance_from_center)
                gradient_mapping[geometry.Coord(column_value, row_value)] = color
    elif direction == Gradient.Direction.DIAGONAL:
        diagonal_span = (row_span * geometry.TERMINAL_ROW_SCALE) + column_span
        for row_value in range(min_row, max_row + 1):
            for column_value in range(min_column, max_column + 1):
                fraction = ((row_value - min_row) * geometry.TERMINAL_ROW_SCALE) + (column_value - min_column)
                fraction = fraction / diagonal_span if diagonal_span else 0
                color = self.get_color_at_fraction(fraction)
                gradient_mapping[geometry.Coord(column_value, row_value)] = color

    return gradient_mapping

get_color_at_fraction(fraction)

Return the precomputed spectrum color corresponding to a normalized fraction.

The fraction is matched to the nearest precomputed spectrum color from start to end. Ties use Python's round-to-even behavior. A fraction of 0 returns the first color and 1 returns the final color.

Parameters:

Name Type Description Default
fraction float

The fraction of the gradient to get the color for.

required

Returns:

Name Type Description
Color Color

The color at the fraction of the gradient.

Raises:

Type Description
TypeError

If fraction is not an integer or float.

ValueError

If fraction is not finite or outside the inclusive range from 0 to 1.

Source code in terminaltexteffects/utils/graphics.py
def get_color_at_fraction(self, fraction: float) -> Color:
    """Return the precomputed spectrum color corresponding to a normalized fraction.

    The fraction is matched to the nearest precomputed spectrum color from start
    to end. Ties use Python's round-to-even behavior. A fraction of `0` returns
    the first color and `1` returns the final color.

    Args:
        fraction (float): The fraction of the gradient to get the color for.

    Returns:
        Color: The color at the fraction of the gradient.

    Raises:
        TypeError: If `fraction` is not an integer or float.
        ValueError: If `fraction` is not finite or outside the inclusive range from `0` to `1`.

    """
    if isinstance(fraction, bool) or not isinstance(fraction, (int, float)):
        msg = "Fraction must be an integer or float."
        raise TypeError(msg)
    if not math.isfinite(fraction):
        msg = "Fraction must be finite."
        raise ValueError(msg)
    if fraction < 0 or fraction > 1:
        msg = "Fraction must be 0 <= fraction <= 1."
        raise ValueError(msg)
    spectrum_index = round(fraction * (len(self.spectrum) - 1))
    return self.spectrum[spectrum_index]