Repository: ManimCommunity/manim
File: manim/utils/bezier.py (Python)

Code before:
    return np.array([a1, s1, p, p, s2, a2])


def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray:
    """Subdivide a quadratic bezier n times

    Parameters
    ----------
    points : np.array
        The control points of the bezier curve in form [a1, h1, b1]

    n : int
        The number of times to subdivide the bezier curve

    Returns
    -------
        returns the new points for the bezier curve in the form [a1, h1, b1, a2, h2, b2, ...]
    """
    beziers = []
    current = points
    for i in range(n, 0, -1):
        tmp = split_quadratic_bezier(current, 1 / i)
        beziers.append(tmp[:3])
        current = tmp[3:]
    return np.asarray(beziers).reshape(-1, 3)


# Linear interpolation variants


def interpolate(start: int, end: int, alpha: float) -> float:
    return (1 - alpha) * start + alpha * end


def integer_interpolate(
    start: float,
    end: float,
    alpha: float,
) -> tuple[int, float]:
    """
    Alpha is a float between 0 and 1.  This returns
    an integer between start and end (inclusive) representing
    appropriate interpolation between them, along with a
    "residue" representing a new proportion between the
    returned integer and the next one of the
    list.

    For example, if start=0, end=10, alpha=0.46, This
    would return (4, 0.6).
    """
    if alpha >= 1:

Review comment (performance):
One more or less serious question: if, say, `n = 5` is passed, the `range` here is `[5, 4, 3, 2, 1]`, which splits the curve first at 1/5, then 1/4 of the remaining etc -- until `i = 1` is reached, which is when `split_quadratic_bezier(current, 1.0)` is called. Is that really helpful? Instead of having `subdivide_quadratic_bezier` construct `n` splits (and thus `n+1` subcurves, where the last split is actually just the last point), wouldn't it make more sense, semantically, to have the function construct `n-1` splits such that the curve is *subdivided* into `n` pieces?

Code after:


def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray:
    """Subdivide a quadratic Bézier curve into ``n`` subcurves which have the same shape.

    The points at which the curve is split are located at the
    arguments :math:`t = i/n` for :math:`i = 1, ..., n-1`.

    Parameters
    ----------
    points
        The control points of the Bézier curve in form ``[a1, h1, b1]``

    n
        The number of curves to subdivide the Bézier curve into

    Returns
    -------
        The new points for the Bézier curve in the form ``[a1, h1, b1, a2, h2, b2, ...]``

    .. image:: /_static/bezier_subdivision_example.png

    """
    beziers = []
    current = points
    for i in range(n, 0, -1):
        tmp = split_quadratic_bezier(current, 1 / i)
        beziers.append(tmp[:3])
        current = tmp[3:]
    return np.asarray(beziers).reshape(-1, 3)


# Linear interpolation variants


def interpolate(start: int, end: int, alpha: float) -> float:
    return (1 - alpha) * start + alpha * end


def integer_interpolate(
    start: float,
    end: float,
    alpha: float,
) -> tuple[int, float]:
    """
    Alpha is a float between 0 and 1.  This returns
    an integer between start and end (inclusive) representing
    appropriate interpolation between them, along with a
    "residue" representing a new proportion between the
    returned integer and the next one of the
    list.