From 9957c4757dfeb3d9dca875a3755b31c30b2b23de Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 13:18:46 +0100 Subject: [PATCH 01/26] Initial type work --- manim/mobject/three_d/three_dimensions.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index ccbc4dea91..2dff79ea1f 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -48,7 +48,14 @@ class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL): - def __init__(self, shade_in_3d: bool = True, **kwargs): + u_index: int + v_index: int + u1: float + u2: float + v1: float + v2: float + + def __init__(self, shade_in_3d: bool = True, **kwargs: Any): super().__init__(shade_in_3d=shade_in_3d, **kwargs) @@ -109,7 +116,7 @@ def __init__( func: Callable[[float, float], np.ndarray], u_range: Sequence[float] = [0, 1], v_range: Sequence[float] = [0, 1], - resolution: Sequence[int] = 32, + resolution: Sequence[int] | int = 32, surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, @@ -226,7 +233,7 @@ def set_fill_by_value( axes: Mobject, colorscale: list[ParsableManimColor] | ParsableManimColor | None = None, axis: int = 2, - **kwargs, + **kwargs: Any, ) -> Self: """Sets the color of each mobject of a parametric surface to a color relative to its axis-value. From 5cef033bbe4d5b0c58814688a075eab3c8b3d410 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 13:30:33 +0100 Subject: [PATCH 02/26] More kwargs Any --- manim/mobject/three_d/three_dimensions.py | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 2dff79ea1f..3b1733780f 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -393,7 +393,7 @@ def __init__( resolution: Sequence[int] | None = None, u_range: Sequence[float] = (0, TAU), v_range: Sequence[float] = (0, PI), - **kwargs, + **kwargs: Any, ) -> None: if config.renderer == RendererType.OPENGL: res_value = (101, 51) @@ -467,7 +467,7 @@ def __init__( radius: float = DEFAULT_DOT_RADIUS, color: ParsableManimColor = WHITE, resolution: tuple[int, int] = (8, 8), - **kwargs, + **kwargs: Any, ) -> None: super().__init__(center=point, radius=radius, resolution=resolution, **kwargs) self.set_color(color) @@ -509,7 +509,7 @@ def __init__( fill_opacity: float = 0.75, fill_color: ParsableManimColor = BLUE, stroke_width: float = 0, - **kwargs, + **kwargs: Any, ) -> None: self.side_length = side_length super().__init__( @@ -561,7 +561,9 @@ def construct(self): """ def __init__( - self, dimensions: tuple[float, float, float] | np.ndarray = [3, 2, 1], **kwargs + self, + dimensions: tuple[float, float, float] | np.ndarray = [3, 2, 1], + **kwargs: Any, ) -> None: self.dimensions = dimensions super().__init__(**kwargs) @@ -781,7 +783,7 @@ def __init__( v_range: Sequence[float] = [0, TAU], show_ends: bool = True, resolution: Sequence[int] = (24, 24), - **kwargs, + **kwargs: Any, ) -> None: self._height = height self.radius = radius @@ -939,7 +941,7 @@ def __init__( thickness: float = 0.02, color: ParsableManimColor | None = None, resolution: int | Sequence[int] = 24, - **kwargs, + **kwargs: Any, ): self.thickness = thickness self.resolution = (2, resolution) if isinstance(resolution, int) else resolution @@ -952,7 +954,7 @@ def __init__( self.set_color(color) def set_start_and_end_attrs( - self, start: np.ndarray, end: np.ndarray, **kwargs + self, start: np.ndarray, end: np.ndarray, **kwargs: Any ) -> None: """Sets the start and end points of the line. @@ -1038,7 +1040,7 @@ def parallel_to( line: Line3D, point: Point3DLike = ORIGIN, length: float = 5, - **kwargs, + **kwargs: Any, ) -> Line3D: """Returns a line parallel to another line going through a given point. @@ -1086,7 +1088,7 @@ def perpendicular_to( line: Line3D, point: Vector3DLike = ORIGIN, length: float = 5, - **kwargs, + **kwargs: Any, ) -> Line3D: """Returns a line perpendicular to another line going through a given point. @@ -1181,7 +1183,7 @@ def __init__( base_radius: float = 0.08, color: ParsableManimColor = WHITE, resolution: int | Sequence[int] = 24, - **kwargs, + **kwargs: Any, ) -> None: super().__init__( start=start, @@ -1251,7 +1253,7 @@ def __init__( u_range: Sequence[float] = (0, TAU), v_range: Sequence[float] = (0, TAU), resolution: tuple[int, int] | None = None, - **kwargs, + **kwargs: Any, ) -> None: if config.renderer == RendererType.OPENGL: res_value = (101, 101) From 4e5cdeb650a1c429987656ec57d673a09232c89f Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 13:30:59 +0100 Subject: [PATCH 03/26] More typing --- manim/mobject/three_d/three_dimensions.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 3b1733780f..a9da68c33e 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -733,7 +733,7 @@ def get_direction(self) -> np.ndarray: """ return self.direction - def _set_start_and_end_attributes(self, direction): + def _set_start_and_end_attributes(self, direction: Vector3DLike) -> None: normalized_direction = direction * np.linalg.norm(direction) start = self.base_circle.get_center() @@ -822,6 +822,7 @@ def func(self, u: float, v: float) -> np.ndarray: def add_bases(self) -> None: """Adds the end caps of the cylinder.""" + opacity: float if config.renderer == RendererType.OPENGL: color = self.color opacity = self.opacity @@ -944,7 +945,9 @@ def __init__( **kwargs: Any, ): self.thickness = thickness - self.resolution = (2, resolution) if isinstance(resolution, int) else resolution + self.resolution: Sequence[int] = ( + (2, resolution) if isinstance(resolution, int) else resolution + ) start = np.array(start, dtype=np.float64) end = np.array(end, dtype=np.float64) From 628b4ed39f45022f1887a7866917da989fc0b17e Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 22:45:07 +0100 Subject: [PATCH 04/26] _get_u_values_and_v_values cleaned --- manim/mobject/three_d/three_dimensions.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index a9da68c33e..5976a9e7c1 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -40,7 +40,6 @@ ParsableManimColor, interpolate_color, ) -from manim.utils.iterables import tuplify from manim.utils.space_ops import normalize, perpendicular_bisector, z_to_vector if TYPE_CHECKING: @@ -158,11 +157,10 @@ def func(self, u: float, v: float) -> np.ndarray: return self._func(u, v) def _get_u_values_and_v_values(self) -> tuple[np.ndarray, np.ndarray]: - res = tuplify(self.resolution) - if len(res) == 1: - u_res = v_res = res[0] + if isinstance(self.resolution, int): + u_res = v_res = self.resolution else: - u_res, v_res = res + u_res, v_res = self.resolution[0:2] u_values = np.linspace(*self.u_range, u_res + 1) v_values = np.linspace(*self.v_range, v_res + 1) From 86760fad1759e2587ea87429f81dd97922446968 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 22:49:10 +0100 Subject: [PATCH 05/26] self.checkerboard_colors --- manim/mobject/three_d/three_dimensions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 5976a9e7c1..4393d45409 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -17,7 +17,7 @@ ] from collections.abc import Callable, Iterable, Sequence -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal import numpy as np from typing_extensions import Self @@ -119,7 +119,10 @@ def __init__( surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, - checkerboard_colors: Sequence[ParsableManimColor] | bool = [BLUE_D, BLUE_E], + checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = [ + BLUE_D, + BLUE_E, + ], stroke_color: ParsableManimColor = LIGHT_GREY, stroke_width: float = 0.5, should_make_jagged: bool = False, @@ -138,7 +141,7 @@ def __init__( self.resolution = resolution self.surface_piece_config = surface_piece_config if checkerboard_colors: - self.checkerboard_colors: list[ManimColor] = [ + self.checkerboard_colors: list[ManimColor] | Literal[False] = [ ManimColor(x) for x in checkerboard_colors ] else: From 0ce74d6b291afb4c4716265e822db360a632e6f6 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 22:54:53 +0100 Subject: [PATCH 06/26] Simplify code --- manim/mobject/three_d/three_dimensions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 4393d45409..6e882e87ff 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -140,12 +140,11 @@ def __init__( ) self.resolution = resolution self.surface_piece_config = surface_piece_config - if checkerboard_colors: - self.checkerboard_colors: list[ManimColor] | Literal[False] = [ - ManimColor(x) for x in checkerboard_colors - ] - else: + self.checkerboard_colors: list[ManimColor] | Literal[False] + if checkerboard_colors is False: self.checkerboard_colors = checkerboard_colors + else: + self.checkerboard_colors = [ManimColor(i) for i in checkerboard_colors] self.should_make_jagged = should_make_jagged self.pre_function_handle_to_anchor_scale_factor = ( pre_function_handle_to_anchor_scale_factor From 9c251a928276774b6f474388d8b5f93ad8261359 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 22:56:15 +0100 Subject: [PATCH 07/26] colorscale --- manim/mobject/three_d/three_dimensions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 6e882e87ff..1b3213dec0 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -231,7 +231,9 @@ def set_fill_by_checkerboard( def set_fill_by_value( self, axes: Mobject, - colorscale: list[ParsableManimColor] | ParsableManimColor | None = None, + colorscale: list[ParsableManimColor] + | list[tuple[ParsableManimColor, float]] + | None = None, axis: int = 2, **kwargs: Any, ) -> Self: From e8c2ea1b27b2d63134b25f09d23d0f26cb6cad6f Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 22:58:26 +0100 Subject: [PATCH 08/26] new_colors --- manim/mobject/three_d/three_dimensions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 1b3213dec0..8cc3e7cc5f 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -297,14 +297,15 @@ def param_surface(u, v): return self ranges = [axes.x_range, axes.y_range, axes.z_range] - + assert isinstance(colorscale, list) + new_colors: list[ManimColor] if type(colorscale[0]) is tuple: new_colors, pivots = [ - [i for i, j in colorscale], + [ManimColor(i) for i, j in colorscale], [j for i, j in colorscale], ] else: - new_colors = colorscale + new_colors = [ManimColor(i) for i in colorscale] pivot_min = ranges[axis][0] pivot_max = ranges[axis][1] From 246832bf1e4cdbeb53d4a07e127b14b6b086e29f Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 23:04:13 +0100 Subject: [PATCH 09/26] Ugly hacks to make the opengl objects behave --- manim/mobject/three_d/three_dimensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 8cc3e7cc5f..f56da320c7 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -335,6 +335,7 @@ def param_surface(u, v): color_index, ) if config.renderer == RendererType.OPENGL: + assert isinstance(mob, OpenGLMobject) mob.set_color(mob_color, recurse=False) elif config.renderer == RendererType.CAIRO: mob.set_color(mob_color, family=False) @@ -827,6 +828,7 @@ def add_bases(self) -> None: """Adds the end caps of the cylinder.""" opacity: float if config.renderer == RendererType.OPENGL: + assert isinstance(self, OpenGLMobject) color = self.color opacity = self.opacity elif config.renderer == RendererType.CAIRO: From 09bc7900c54d61e69d163147bfa3c8d5f58d05fe Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 23:04:23 +0100 Subject: [PATCH 10/26] checkerboard_colors --- manim/mobject/three_d/three_dimensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index f56da320c7..915ee73be1 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -625,7 +625,7 @@ def __init__( show_base: bool = False, v_range: Sequence[float] = [0, TAU], u_min: float = 0, - checkerboard_colors: bool = False, + checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = False, **kwargs: Any, ) -> None: self.direction = direction From b95b75619fd878cff0c6c19c99c464f314e367e9 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 23:06:34 +0100 Subject: [PATCH 11/26] Ignored a single type error --- manim/mobject/three_d/three_dimensions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 915ee73be1..326297a4c9 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -296,7 +296,11 @@ def param_surface(u, v): ) return self - ranges = [axes.x_range, axes.y_range, axes.z_range] + # TODO: Handle this type error that has been ignored + # error: List item 0 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] + # error: List item 1 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] + # error: List item 2 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] + ranges: list[Sequence[float]] = [axes.x_range, axes.y_range, axes.z_range] # type: ignore[list-item] assert isinstance(colorscale, list) new_colors: list[ManimColor] if type(colorscale[0]) is tuple: From fcdce8659e1ed86fa67e07288e0786cd58206aff Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 23:14:01 +0100 Subject: [PATCH 12/26] Ignored the last type error --- manim/mobject/three_d/three_dimensions.py | 3 ++- mypy.ini | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 326297a4c9..a08b575e54 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -201,7 +201,8 @@ def _setup_in_uv_space(self) -> None: ) self.add(*faces) if self.checkerboard_colors: - self.set_fill_by_checkerboard(*self.checkerboard_colors) + # error: Argument 1 to "set_fill_by_checkerboard" of "Surface" has incompatible type "*list[ManimColor]"; expected "Iterable[ManimColor | int | str | Any | tuple[int, int, int] | Any | tuple[float, float, float] | Any | tuple[int, int, int, int] | Any | tuple[float, float, float, float]]" [arg-type] + self.set_fill_by_checkerboard(*self.checkerboard_colors) # type: ignore[arg-type] def set_fill_by_checkerboard( self, *colors: Iterable[ParsableManimColor], opacity: float | None = None diff --git a/mypy.ini b/mypy.ini index 4a96a6d7f8..2528c940e9 100644 --- a/mypy.ini +++ b/mypy.ini @@ -110,7 +110,7 @@ ignore_errors = True ignore_errors = True [mypy-manim.mobject.three_d.three_dimensions] -ignore_errors = True +ignore_errors = False [mypy-manim.mobject.types.image_mobject] ignore_errors = True From 092a44a497f27248b35c6f4ee1615eeda8acd31c Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 1 Dec 2025 23:28:38 +0100 Subject: [PATCH 13/26] Remove entry from mypy.ini --- mypy.ini | 3 --- 1 file changed, 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index 2528c940e9..91ce5479c8 100644 --- a/mypy.ini +++ b/mypy.ini @@ -109,9 +109,6 @@ ignore_errors = True [mypy-manim.mobject.table] ignore_errors = True -[mypy-manim.mobject.three_d.three_dimensions] -ignore_errors = False - [mypy-manim.mobject.types.image_mobject] ignore_errors = True From fdd3194be3c501e2be21edc43bbfca6146fc0ade Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:28:31 +0100 Subject: [PATCH 14/26] set_fill_by_checkerboard --- manim/mobject/three_d/three_dimensions.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index a08b575e54..c0131a7c14 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -16,7 +16,7 @@ "Torus", ] -from collections.abc import Callable, Iterable, Sequence +from collections.abc import Callable, Sequence from typing import TYPE_CHECKING, Any, Literal import numpy as np @@ -201,11 +201,10 @@ def _setup_in_uv_space(self) -> None: ) self.add(*faces) if self.checkerboard_colors: - # error: Argument 1 to "set_fill_by_checkerboard" of "Surface" has incompatible type "*list[ManimColor]"; expected "Iterable[ManimColor | int | str | Any | tuple[int, int, int] | Any | tuple[float, float, float] | Any | tuple[int, int, int, int] | Any | tuple[float, float, float, float]]" [arg-type] - self.set_fill_by_checkerboard(*self.checkerboard_colors) # type: ignore[arg-type] + self.set_fill_by_checkerboard(*self.checkerboard_colors) def set_fill_by_checkerboard( - self, *colors: Iterable[ParsableManimColor], opacity: float | None = None + self, *colors: ParsableManimColor, opacity: float | None = None ) -> Self: """Sets the fill_color of each face of :class:`Surface` in an alternating pattern. From a830c8e3c1cbf9b49d6cd88b4d88b45bdee795ca Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:28:47 +0100 Subject: [PATCH 15/26] resolution --- manim/mobject/three_d/three_dimensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index c0131a7c14..abd88e21f5 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -115,7 +115,7 @@ def __init__( func: Callable[[float, float], np.ndarray], u_range: Sequence[float] = [0, 1], v_range: Sequence[float] = [0, 1], - resolution: Sequence[int] | int = 32, + resolution: int | Sequence[int] = 32, surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, From dfe0d22c8fa4d7ce22eeea58203b48a621c3faa4 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:29:13 +0100 Subject: [PATCH 16/26] u_range and v_range --- manim/mobject/three_d/three_dimensions.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index abd88e21f5..703af781c6 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -113,8 +113,8 @@ def construct(self): def __init__( self, func: Callable[[float, float], np.ndarray], - u_range: Sequence[float] = [0, 1], - v_range: Sequence[float] = [0, 1], + u_range: tuple[float, float] = (0, 1), + v_range: tuple[float, float] = (0, 1), resolution: int | Sequence[int] = 32, surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, @@ -399,8 +399,8 @@ def __init__( center: Point3DLike = ORIGIN, radius: float = 1, resolution: Sequence[int] | None = None, - u_range: Sequence[float] = (0, TAU), - v_range: Sequence[float] = (0, PI), + u_range: tuple[float, float] = (0, TAU), + v_range: tuple[float, float] = (0, PI), **kwargs: Any, ) -> None: if config.renderer == RendererType.OPENGL: @@ -627,7 +627,7 @@ def __init__( height: float = 1, direction: np.ndarray = Z_AXIS, show_base: bool = False, - v_range: Sequence[float] = [0, TAU], + v_range: tuple[float, float] = (0, TAU), u_min: float = 0, checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = False, **kwargs: Any, @@ -638,7 +638,7 @@ def __init__( super().__init__( self.func, v_range=v_range, - u_range=[u_min, np.sqrt(base_radius**2 + height**2)], + u_range=(u_min, np.sqrt(base_radius**2 + height**2)), checkerboard_colors=checkerboard_colors, **kwargs, ) @@ -788,7 +788,7 @@ def __init__( radius: float = 1, height: float = 2, direction: np.ndarray = Z_AXIS, - v_range: Sequence[float] = [0, TAU], + v_range: tuple[float, float] = (0, TAU), show_ends: bool = True, resolution: Sequence[int] = (24, 24), **kwargs: Any, @@ -798,7 +798,7 @@ def __init__( super().__init__( self.func, resolution=resolution, - u_range=[-self._height / 2, self._height / 2], + u_range=(-self._height / 2, self._height / 2), v_range=v_range, **kwargs, ) @@ -1262,8 +1262,8 @@ def __init__( self, major_radius: float = 3, minor_radius: float = 1, - u_range: Sequence[float] = (0, TAU), - v_range: Sequence[float] = (0, TAU), + u_range: tuple[float, float] = (0, TAU), + v_range: tuple[float, float] = (0, TAU), resolution: tuple[int, int] | None = None, **kwargs: Any, ) -> None: From 2fe03d266c907ab51003e1ee0df5a8b948069afd Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:39:07 +0100 Subject: [PATCH 17/26] ThreeDAxes --- manim/mobject/three_d/three_dimensions.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 703af781c6..38cd8e7795 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -43,6 +43,7 @@ from manim.utils.space_ops import normalize, perpendicular_bisector, z_to_vector if TYPE_CHECKING: + from manim.mobject.graphing.coordinate_systems import ThreeDAxes from manim.typing import Point3D, Point3DLike, Vector3DLike @@ -230,7 +231,7 @@ def set_fill_by_checkerboard( def set_fill_by_value( self, - axes: Mobject, + axes: ThreeDAxes, colorscale: list[ParsableManimColor] | list[tuple[ParsableManimColor, float]] | None = None, @@ -296,11 +297,7 @@ def param_surface(u, v): ) return self - # TODO: Handle this type error that has been ignored - # error: List item 0 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] - # error: List item 1 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] - # error: List item 2 has incompatible type "MethodType"; expected "Sequence[float]" [list-item] - ranges: list[Sequence[float]] = [axes.x_range, axes.y_range, axes.z_range] # type: ignore[list-item] + ranges = [axes.x_range, axes.y_range, axes.z_range] assert isinstance(colorscale, list) new_colors: list[ManimColor] if type(colorscale[0]) is tuple: @@ -310,9 +307,11 @@ def param_surface(u, v): ] else: new_colors = [ManimColor(i) for i in colorscale] + current_range = ranges[axis] - pivot_min = ranges[axis][0] - pivot_max = ranges[axis][1] + assert current_range is not None + pivot_min = current_range[0] + pivot_max = current_range[1] pivot_frequency = (pivot_max - pivot_min) / (len(new_colors) - 1) pivots = np.arange( start=pivot_min, From a2db13485b9543735890eb26ce2b9571a2ad5926 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:56:53 +0100 Subject: [PATCH 18/26] Is tuple a color? --- manim/mobject/three_d/three_dimensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 38cd8e7795..e76d22157e 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -300,7 +300,7 @@ def param_surface(u, v): ranges = [axes.x_range, axes.y_range, axes.z_range] assert isinstance(colorscale, list) new_colors: list[ManimColor] - if type(colorscale[0]) is tuple: + if type(colorscale[0]) is tuple and len(colorscale[0]) == 2: new_colors, pivots = [ [ManimColor(i) for i, j in colorscale], [j for i, j in colorscale], From 0333a11abc28c65deaee9a8ea849addac7fe508e Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:57:37 +0100 Subject: [PATCH 19/26] resolution --- manim/mobject/three_d/three_dimensions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index e76d22157e..45b0f2affc 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -397,7 +397,7 @@ def __init__( self, center: Point3DLike = ORIGIN, radius: float = 1, - resolution: Sequence[int] | None = None, + resolution: int | Sequence[int] | None = None, u_range: tuple[float, float] = (0, TAU), v_range: tuple[float, float] = (0, PI), **kwargs: Any, @@ -473,7 +473,7 @@ def __init__( point: list | np.ndarray = ORIGIN, radius: float = DEFAULT_DOT_RADIUS, color: ParsableManimColor = WHITE, - resolution: tuple[int, int] = (8, 8), + resolution: int | tuple[int, int] | None = (8, 8), **kwargs: Any, ) -> None: super().__init__(center=point, radius=radius, resolution=resolution, **kwargs) @@ -789,7 +789,7 @@ def __init__( direction: np.ndarray = Z_AXIS, v_range: tuple[float, float] = (0, TAU), show_ends: bool = True, - resolution: Sequence[int] = (24, 24), + resolution: int | Sequence[int] = (24, 24), **kwargs: Any, ) -> None: self._height = height @@ -949,7 +949,7 @@ def __init__( end: np.ndarray = RIGHT, thickness: float = 0.02, color: ParsableManimColor | None = None, - resolution: int | Sequence[int] = 24, + resolution: int | tuple[int, int] = 24, **kwargs: Any, ): self.thickness = thickness @@ -1193,7 +1193,7 @@ def __init__( height: float = 0.3, base_radius: float = 0.08, color: ParsableManimColor = WHITE, - resolution: int | Sequence[int] = 24, + resolution: int | tuple[int, int] = 24, **kwargs: Any, ) -> None: super().__init__( From d74a883f4924d47b48e1190438f79d640b6fe919 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 17:59:20 +0100 Subject: [PATCH 20/26] Point3D and Vector3D --- manim/mobject/three_d/three_dimensions.py | 42 +++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 45b0f2affc..821070b0f1 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -44,7 +44,7 @@ if TYPE_CHECKING: from manim.mobject.graphing.coordinate_systems import ThreeDAxes - from manim.typing import Point3D, Point3DLike, Vector3DLike + from manim.typing import Point3D, Point3DLike, Vector3D, Vector3DLike class ThreeDVMobject(VMobject, metaclass=ConvertToOpenGL): @@ -423,12 +423,12 @@ def __init__( self.shift(center) - def func(self, u: float, v: float) -> np.ndarray: + def func(self, u: float, v: float) -> Point3D: """The z values defining the :class:`Sphere` being plotted. Returns ------- - :class:`numpy.array` + :class:`Point3D` The z values defining the :class:`Sphere`. """ return self.radius * np.array( @@ -470,7 +470,7 @@ def construct(self): def __init__( self, - point: list | np.ndarray = ORIGIN, + point: Point3D = ORIGIN, radius: float = DEFAULT_DOT_RADIUS, color: ParsableManimColor = WHITE, resolution: int | tuple[int, int] | None = (8, 8), @@ -569,7 +569,7 @@ def construct(self): def __init__( self, - dimensions: tuple[float, float, float] | np.ndarray = [3, 2, 1], + dimensions: Vector3DLike = [3, 2, 1], **kwargs: Any, ) -> None: self.dimensions = dimensions @@ -624,14 +624,14 @@ def __init__( self, base_radius: float = 1, height: float = 1, - direction: np.ndarray = Z_AXIS, + direction: Vector3DLike = Z_AXIS, show_base: bool = False, v_range: tuple[float, float] = (0, TAU), u_min: float = 0, checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = False, **kwargs: Any, ) -> None: - self.direction = direction + self.direction = np.array(direction) self.theta = PI - np.arctan(base_radius / height) super().__init__( @@ -658,7 +658,7 @@ def __init__( self._rotate_to_direction() - def func(self, u: float, v: float) -> np.ndarray: + def func(self, u: float, v: float) -> Point3D: """Converts from spherical coordinates to cartesian. Parameters @@ -683,10 +683,10 @@ def func(self, u: float, v: float) -> np.ndarray: ], ) - def get_start(self) -> np.ndarray: + def get_start(self) -> Point3D: return self.start_point.get_center() - def get_end(self) -> np.ndarray: + def get_end(self) -> Point3D: return self.end_point.get_center() def _rotate_to_direction(self) -> None: @@ -719,7 +719,7 @@ def _rotate_to_direction(self) -> None: self._current_theta = theta self._current_phi = phi - def set_direction(self, direction: np.ndarray) -> None: + def set_direction(self, direction: Vector3DLike) -> None: """Changes the direction of the apex of the :class:`Cone`. Parameters @@ -727,10 +727,10 @@ def set_direction(self, direction: np.ndarray) -> None: direction The direction of the apex. """ - self.direction = direction + self.direction = np.array(direction) self._rotate_to_direction() - def get_direction(self) -> np.ndarray: + def get_direction(self) -> Vector3D: """Returns the current direction of the apex of the :class:`Cone`. Returns @@ -740,7 +740,7 @@ def get_direction(self) -> np.ndarray: """ return self.direction - def _set_start_and_end_attributes(self, direction: Vector3DLike) -> None: + def _set_start_and_end_attributes(self, direction: Vector3D) -> None: normalized_direction = direction * np.linalg.norm(direction) start = self.base_circle.get_center() @@ -786,7 +786,7 @@ def __init__( self, radius: float = 1, height: float = 2, - direction: np.ndarray = Z_AXIS, + direction: Vector3DLike = Z_AXIS, v_range: tuple[float, float] = (0, TAU), show_ends: bool = True, resolution: int | Sequence[int] = (24, 24), @@ -886,7 +886,7 @@ def _rotate_to_direction(self) -> None: self._current_theta = theta self._current_phi = phi - def set_direction(self, direction: np.ndarray) -> None: + def set_direction(self, direction: Vector3DLike) -> None: """Sets the direction of the central axis of the :class:`Cylinder`. Parameters @@ -945,8 +945,8 @@ def construct(self): def __init__( self, - start: np.ndarray = LEFT, - end: np.ndarray = RIGHT, + start: Point3DLike = LEFT, + end: Point3DLike = RIGHT, thickness: float = 0.02, color: ParsableManimColor | None = None, resolution: int | tuple[int, int] = 24, @@ -983,7 +983,7 @@ def set_start_and_end_attrs( rough_end = self.pointify(end) self.vect = rough_end - rough_start self.length = np.linalg.norm(self.vect) - self.direction = normalize(self.vect) + self.direction: Vector3D = normalize(self.vect) # Now that we know the direction between them, # we can the appropriate boundary point from # start and end, if they're mobjects @@ -1187,8 +1187,8 @@ def construct(self): def __init__( self, - start: np.ndarray = LEFT, - end: np.ndarray = RIGHT, + start: Point3DLike = LEFT, + end: Point3DLike = RIGHT, thickness: float = 0.02, height: float = 0.3, base_radius: float = 0.08, From 81f4c9545cf0ca7e974001848378274f60bd37bc Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 18:03:11 +0100 Subject: [PATCH 21/26] More with Point3D --- manim/mobject/three_d/three_dimensions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 821070b0f1..369aad2afe 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -965,7 +965,7 @@ def __init__( self.set_color(color) def set_start_and_end_attrs( - self, start: np.ndarray, end: np.ndarray, **kwargs: Any + self, start: Point3DLike, end: Point3DLike, **kwargs: Any ) -> None: """Sets the start and end points of the line. @@ -1025,7 +1025,7 @@ def pointify( return mob.get_boundary_point(direction) return np.array(mob_or_point) - def get_start(self) -> np.ndarray: + def get_start(self) -> Point3D: """Returns the starting point of the :class:`Line3D`. Returns @@ -1035,7 +1035,7 @@ def get_start(self) -> np.ndarray: """ return self.start - def get_end(self) -> np.ndarray: + def get_end(self) -> Point3D: """Returns the ending point of the :class:`Line3D`. Returns @@ -1097,7 +1097,7 @@ def construct(self): def perpendicular_to( cls, line: Line3D, - point: Vector3DLike = ORIGIN, + point: Point3DLike = ORIGIN, length: float = 5, **kwargs: Any, ) -> Line3D: @@ -1263,7 +1263,7 @@ def __init__( minor_radius: float = 1, u_range: tuple[float, float] = (0, TAU), v_range: tuple[float, float] = (0, TAU), - resolution: tuple[int, int] | None = None, + resolution: int | tuple[int, int] | None = None, **kwargs: Any, ) -> None: if config.renderer == RendererType.OPENGL: @@ -1283,7 +1283,7 @@ def __init__( **kwargs, ) - def func(self, u: float, v: float) -> np.ndarray: + def func(self, u: float, v: float) -> Point3D: """The z values defining the :class:`Torus` being plotted. Returns From d9c60437b80bf50725c6e3c573fbecafa43f0b92 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 21:59:30 +0100 Subject: [PATCH 22/26] resolution --- manim/mobject/three_d/three_dimensions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 369aad2afe..fb11cea2a7 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -163,7 +163,7 @@ def _get_u_values_and_v_values(self) -> tuple[np.ndarray, np.ndarray]: if isinstance(self.resolution, int): u_res = v_res = self.resolution else: - u_res, v_res = self.resolution[0:2] + u_res, v_res = self.resolution u_values = np.linspace(*self.u_range, u_res + 1) v_values = np.linspace(*self.v_range, v_res + 1) @@ -789,7 +789,7 @@ def __init__( direction: Vector3DLike = Z_AXIS, v_range: tuple[float, float] = (0, TAU), show_ends: bool = True, - resolution: int | Sequence[int] = (24, 24), + resolution: int | tuple[int, int] = (24, 24), **kwargs: Any, ) -> None: self._height = height @@ -953,7 +953,7 @@ def __init__( **kwargs: Any, ): self.thickness = thickness - self.resolution: Sequence[int] = ( + self.resolution: tuple[int, int] = ( (2, resolution) if isinstance(resolution, int) else resolution ) From b458f03e776e6030fff72c32032a686dc461a529 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 22:07:19 +0100 Subject: [PATCH 23/26] colorscale --- manim/mobject/three_d/three_dimensions.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index fb11cea2a7..cc5eda6d56 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -16,7 +16,7 @@ "Torus", ] -from collections.abc import Callable, Sequence +from collections.abc import Callable, Iterable, Sequence from typing import TYPE_CHECKING, Any, Literal import numpy as np @@ -232,8 +232,8 @@ def set_fill_by_checkerboard( def set_fill_by_value( self, axes: ThreeDAxes, - colorscale: list[ParsableManimColor] - | list[tuple[ParsableManimColor, float]] + colorscale: Iterable[ParsableManimColor] + | Iterable[tuple[ParsableManimColor, float]] | None = None, axis: int = 2, **kwargs: Any, @@ -296,17 +296,18 @@ def param_surface(u, v): "the surface fill color has not been changed" ) return self + colorscale_list = list(colorscale) ranges = [axes.x_range, axes.y_range, axes.z_range] - assert isinstance(colorscale, list) + assert isinstance(colorscale_list, list) new_colors: list[ManimColor] - if type(colorscale[0]) is tuple and len(colorscale[0]) == 2: + if type(colorscale_list[0]) is tuple and len(colorscale_list[0]) == 2: new_colors, pivots = [ - [ManimColor(i) for i, j in colorscale], - [j for i, j in colorscale], + [ManimColor(i) for i, j in colorscale_list], + [j for i, j in colorscale_list], ] else: - new_colors = [ManimColor(i) for i in colorscale] + new_colors = [ManimColor(i) for i in colorscale_list] current_range = ranges[axis] assert current_range is not None From c530d6f4fe4756a2b9bb92a32b7c4990535ed933 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 22:12:20 +0100 Subject: [PATCH 24/26] checkerboard_colors Iterable --- manim/mobject/three_d/three_dimensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index cc5eda6d56..53d8e98c83 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -120,7 +120,7 @@ def __init__( surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, - checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = [ + checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = [ BLUE_D, BLUE_E, ], @@ -629,7 +629,7 @@ def __init__( show_base: bool = False, v_range: tuple[float, float] = (0, TAU), u_min: float = 0, - checkerboard_colors: Sequence[ParsableManimColor] | Literal[False] = False, + checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = False, **kwargs: Any, ) -> None: self.direction = np.array(direction) From d5c54cf5fa9c71d43d490700218109c554f45bfb Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 22:22:28 +0100 Subject: [PATCH 25/26] Breaking change: checkerboard_colors can now be set to None --- manim/mobject/three_d/three_dimensions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 53d8e98c83..8c5e243d0f 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -17,7 +17,7 @@ ] from collections.abc import Callable, Iterable, Sequence -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any import numpy as np from typing_extensions import Self @@ -120,7 +120,7 @@ def __init__( surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, - checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = [ + checkerboard_colors: Iterable[ParsableManimColor] | None = [ BLUE_D, BLUE_E, ], @@ -141,8 +141,8 @@ def __init__( ) self.resolution = resolution self.surface_piece_config = surface_piece_config - self.checkerboard_colors: list[ManimColor] | Literal[False] - if checkerboard_colors is False: + self.checkerboard_colors: list[ManimColor] | None + if checkerboard_colors is None: self.checkerboard_colors = checkerboard_colors else: self.checkerboard_colors = [ManimColor(i) for i in checkerboard_colors] @@ -629,7 +629,7 @@ def __init__( show_base: bool = False, v_range: tuple[float, float] = (0, TAU), u_min: float = 0, - checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = False, + checkerboard_colors: Iterable[ParsableManimColor] | None = None, **kwargs: Any, ) -> None: self.direction = np.array(direction) From c6cb064d44cf5a976c68bb8daa011a50c9f92c2f Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Sat, 6 Dec 2025 22:30:39 +0100 Subject: [PATCH 26/26] Revert "Breaking change: checkerboard_colors can now be set to None" This reverts commit d5c54cf5fa9c71d43d490700218109c554f45bfb. --- manim/mobject/three_d/three_dimensions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 8c5e243d0f..53d8e98c83 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -17,7 +17,7 @@ ] from collections.abc import Callable, Iterable, Sequence -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal import numpy as np from typing_extensions import Self @@ -120,7 +120,7 @@ def __init__( surface_piece_config: dict = {}, fill_color: ParsableManimColor = BLUE_D, fill_opacity: float = 1.0, - checkerboard_colors: Iterable[ParsableManimColor] | None = [ + checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = [ BLUE_D, BLUE_E, ], @@ -141,8 +141,8 @@ def __init__( ) self.resolution = resolution self.surface_piece_config = surface_piece_config - self.checkerboard_colors: list[ManimColor] | None - if checkerboard_colors is None: + self.checkerboard_colors: list[ManimColor] | Literal[False] + if checkerboard_colors is False: self.checkerboard_colors = checkerboard_colors else: self.checkerboard_colors = [ManimColor(i) for i in checkerboard_colors] @@ -629,7 +629,7 @@ def __init__( show_base: bool = False, v_range: tuple[float, float] = (0, TAU), u_min: float = 0, - checkerboard_colors: Iterable[ParsableManimColor] | None = None, + checkerboard_colors: Iterable[ParsableManimColor] | Literal[False] = False, **kwargs: Any, ) -> None: self.direction = np.array(direction)