Repository: ManimCommunity/manim
File: manim/mobject/types/vectorized_mobject.py (Python)

Code before:
    ) -> float:
        """Returns the proportion along the path of the :class:`VMobject`
        a particular given point is at.

        Parameters
        ----------
        point (typing.Iterable[float])
            The Cartesian coordinates of the point which may or may not lie on the :class:`VMobject`

        Returns
        -------
        float
            The proportion along the path of the :class:`VMobject`.

        Raises
        ------
        :exc:`ValueError`
            If ``point`` does not lie on the curve.
        :exc:`Exception`
            If the :class:`VMobject` has no points.
        """
        self.throw_error_if_no_points()

        nc = self.get_num_curves()
        vmob_length = sum(self.get_nth_curve_length(i) for i in range(nc))
        total_length = 0
        for n in range(nc):
            control_points = self.get_nth_curve_points(n)
            length = self.get_nth_curve_length(n)
            if point_lies_on_bezier(point, control_points):
                t = max(bez_params_from_point(point, control_points))
                total_length += length * t
                break
            else:
                total_length += length
        else:
            raise ValueError(f"Point {point} does not lie on this curve.")
        alpha = total_length / vmob_length

        return alpha

    def get_anchors_and_handles(self) -> typing.Iterable[np.ndarray]:
        """Returns anchors1, handles1, handles2, anchors2,
        where (anchors1[i], handles1[i], handles2[i], anchors2[i])
        will be four points defining a cubic bezier curve
        for any i in range(0, len(anchors1))

        Returns
        -------
        typing.Iterable[np.ndarray]
            Iterable of the anchors and handles.

Review comment (suggestion):
I would expect `total_length` to be the total length of the mobject, so taking inspiration from `point_from_proportion`:
```suggestion
        total_length = self.get_arc_length()
        target_length = 0
```
(you'd also need to update where you use these variables)

Code after:
        return length

    def get_nth_curve_function_with_length(
        self, n: int, sample_points: Optional[int] = None
    ) -> typing.Tuple[typing.Callable[[float], np.ndarray], float]:
        """Returns the expression of the nth curve along with its (approximate) length.

        Parameters
        ----------
        n
            The index of the desired curve.
        sample_points
            The number of points to sample to find the length.

        Returns
        -------
        curve : typing.Callable[[float], np.ndarray]
            The function for the nth curve.
        length : :class:`float`
            The length of the nth curve.
        """

        if sample_points is None:
            sample_points = 10

        curve = self.get_nth_curve_function(n)

        points = np.array([curve(a) for a in np.linspace(0, 1, sample_points)])
        diffs = points[1:] - points[:-1]
        norms = np.apply_along_axis(np.linalg.norm, 1, diffs)

        length = np.sum(norms)

        return curve, length

    def get_num_curves(self) -> int:
        """Returns the number of curves of the vmobject.

        Returns
        -------
        int
            number of curves. of the vmobject.
        """
        nppcc = self.n_points_per_cubic_curve
        return len(self.points) // nppcc

    def get_curve_functions(
        self,
    ) -> typing.Iterable[typing.Callable[[float], np.ndarray]]:
        """Gets the functions for the curves of the mobject.