From 803bc057bb1d5ef79ba6d40013b655b83c2a4c7f Mon Sep 17 00:00:00 2001 From: Bennett Liyanto Date: Wed, 23 Apr 2025 14:07:39 -0400 Subject: [PATCH 1/5] Fix gradient reversal bug (#4198) --- manim/mobject/types/vectorized_mobject.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 321fe4287b..914ed54234 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -257,6 +257,16 @@ def update_rgbas_array( # Match up current rgbas array with the newly calculated # one. 99% of the time they'll be the same. curr_rgbas = getattr(self, array_name) + if isinstance(color, (list, tuple)) and len(color) > 1: + start_rgba = ManimColor.parse(color[0]).to_rgba_with_alpha( + tuplify(opacity)[0] if opacity is not None else curr_rgbas[0, 3] + ) + end_rgba = ManimColor.parse(color[-1]).to_rgba_with_alpha( + tuplify(opacity)[-1] if opacity is not None else curr_rgbas[-1, 3] + ) + # Interpolate from first → last colour so that every vertex receives + # the correct value (length is fixed by the existing array) + rgbas = np.linspace(start_rgba, end_rgba, len(curr_rgbas)) if len(curr_rgbas) < len(rgbas): curr_rgbas = stretch_array_to_length(curr_rgbas, len(rgbas)) setattr(self, array_name, curr_rgbas) From 076e1f5609de7d4cf0e4833ffd0bc9f46b577e58 Mon Sep 17 00:00:00 2001 From: Bennett Liyanto Date: Wed, 23 Apr 2025 16:07:05 -0400 Subject: [PATCH 2/5] fix 2 --- color_test.py | 13 ++++++++++ gradient_demo.py | 28 ++++++++++++++++++++++ manim/mobject/types/vectorized_mobject.py | 16 ++++++------- tests/test_gradient_order.py | 29 +++++++++++++++++++++++ 4 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 color_test.py create mode 100644 gradient_demo.py create mode 100644 tests/test_gradient_order.py diff --git a/color_test.py b/color_test.py new file mode 100644 index 0000000000..0773301fb6 --- /dev/null +++ b/color_test.py @@ -0,0 +1,13 @@ + +import numpy as np +from manim import * + +class ColorTest(Scene): + def construct(self): + line1 = Line(np.array([-1, 0, 0]), np.array([0, 1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) + line2 = Line(np.array([-1, 0, 0]), np.array([1, 0, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) + line3 = Line(np.array([-1, 0, 0]), np.array([0, -1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) + line4 = Line(np.array([-1, 0, 0]), np.array([-1, 1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) + + self.play(Create(line1), Create(line2), Create(line3), Create(line4)) + self.wait(2) diff --git a/gradient_demo.py b/gradient_demo.py new file mode 100644 index 0000000000..a283f93413 --- /dev/null +++ b/gradient_demo.py @@ -0,0 +1,28 @@ +from manim import Scene, Line, DashedLine, BLUE, RED, GREEN, WHITE +from manim.constants import LEFT, RIGHT, UP, DOWN + + +class GradientDemo(Scene): + """ + Quick visual sanity-check: one solid and one dashed line showing that + the first colour in the list is always mapped to the start anchor. + """ + + def construct(self): + # ── Solid 2-stop gradient ─────────────────────────────────────────────── + solid = ( + Line(LEFT * 4 + UP * 2, RIGHT * 4 + UP * 2, stroke_width=24) + .set_stroke([BLUE, RED]) + ) + + # ── Dashed 2-stop gradient (let Manim pick dash count) ───────────────── + dashed = ( + DashedLine( + LEFT * 4 + DOWN * 2, + RIGHT * 4 + DOWN * 2, + stroke_width=24, + ) + .set_stroke([GREEN, WHITE]) + ) + + self.add(solid, dashed) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 914ed54234..cb10426b0c 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -258,15 +258,13 @@ def update_rgbas_array( # one. 99% of the time they'll be the same. curr_rgbas = getattr(self, array_name) if isinstance(color, (list, tuple)) and len(color) > 1: - start_rgba = ManimColor.parse(color[0]).to_rgba_with_alpha( - tuplify(opacity)[0] if opacity is not None else curr_rgbas[0, 3] - ) - end_rgba = ManimColor.parse(color[-1]).to_rgba_with_alpha( - tuplify(opacity)[-1] if opacity is not None else curr_rgbas[-1, 3] - ) - # Interpolate from first → last colour so that every vertex receives - # the correct value (length is fixed by the existing array) - rgbas = np.linspace(start_rgba, end_rgba, len(curr_rgbas)) + rgba_list = [ + ManimColor(c).to_rgba_with_alpha( + opacity if opacity is not None else curr_rgbas[0, 3] + ) + for c in color + ] + rgbas = np.array(rgba_list) if len(curr_rgbas) < len(rgbas): curr_rgbas = stretch_array_to_length(curr_rgbas, len(rgbas)) setattr(self, array_name, curr_rgbas) diff --git a/tests/test_gradient_order.py b/tests/test_gradient_order.py new file mode 100644 index 0000000000..297eea60f7 --- /dev/null +++ b/tests/test_gradient_order.py @@ -0,0 +1,29 @@ +import numpy as np + +from manim import Line, BLUE, RED +from manim.constants import LEFT, RIGHT + + +def _first_and_last_rgb(vmob): + """Return RGB (no alpha) of first & last stroke rows.""" + rgbas = vmob.get_stroke_rgbas() + return rgbas[0, :3], rgbas[-1, :3] + + +def test_gradient_left_to_right(): + """Blue should start at the left, red at the right.""" + seg = Line(LEFT, RIGHT).set_stroke([BLUE, RED], width=4) + first, last = _first_and_last_rgb(seg) + assert np.allclose(first, BLUE.to_rgb()), "Start of gradient is not BLUE" + assert np.allclose(last, RED.to_rgb()), "End of gradient is not RED" + + +def test_gradient_right_to_left(): + """ + Reversing the points must **not** reverse the + colour order supplied by the user. + """ + seg = Line(RIGHT, LEFT).set_stroke([BLUE, RED], width=4) + first, last = _first_and_last_rgb(seg) + assert np.allclose(first, BLUE.to_rgb()), "Start of gradient is not BLUE" + assert np.allclose(last, RED.to_rgb()), "End of gradient is not RED" From 5efd56c250f7e7a8dc2b606a9a4453f783146d12 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:21:20 +0000 Subject: [PATCH 3/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- color_test.py | 19 +++++++++++++++---- gradient_demo.py | 24 +++++++++++------------- tests/test_gradient_order.py | 2 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/color_test.py b/color_test.py index 0773301fb6..53b5f50230 100644 --- a/color_test.py +++ b/color_test.py @@ -1,13 +1,24 @@ +from __future__ import annotations import numpy as np + from manim import * + class ColorTest(Scene): def construct(self): - line1 = Line(np.array([-1, 0, 0]), np.array([0, 1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) - line2 = Line(np.array([-1, 0, 0]), np.array([1, 0, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) - line3 = Line(np.array([-1, 0, 0]), np.array([0, -1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) - line4 = Line(np.array([-1, 0, 0]), np.array([-1, 1, 0]), stroke_width=20).set_color(color=["#7301FF", "#EDCD00"]) + line1 = Line( + np.array([-1, 0, 0]), np.array([0, 1, 0]), stroke_width=20 + ).set_color(color=["#7301FF", "#EDCD00"]) + line2 = Line( + np.array([-1, 0, 0]), np.array([1, 0, 0]), stroke_width=20 + ).set_color(color=["#7301FF", "#EDCD00"]) + line3 = Line( + np.array([-1, 0, 0]), np.array([0, -1, 0]), stroke_width=20 + ).set_color(color=["#7301FF", "#EDCD00"]) + line4 = Line( + np.array([-1, 0, 0]), np.array([-1, 1, 0]), stroke_width=20 + ).set_color(color=["#7301FF", "#EDCD00"]) self.play(Create(line1), Create(line2), Create(line3), Create(line4)) self.wait(2) diff --git a/gradient_demo.py b/gradient_demo.py index a283f93413..3eca8e82f5 100644 --- a/gradient_demo.py +++ b/gradient_demo.py @@ -1,5 +1,7 @@ -from manim import Scene, Line, DashedLine, BLUE, RED, GREEN, WHITE -from manim.constants import LEFT, RIGHT, UP, DOWN +from __future__ import annotations + +from manim import BLUE, GREEN, RED, WHITE, DashedLine, Line, Scene +from manim.constants import DOWN, LEFT, RIGHT, UP class GradientDemo(Scene): @@ -10,19 +12,15 @@ class GradientDemo(Scene): def construct(self): # ── Solid 2-stop gradient ─────────────────────────────────────────────── - solid = ( - Line(LEFT * 4 + UP * 2, RIGHT * 4 + UP * 2, stroke_width=24) - .set_stroke([BLUE, RED]) + solid = Line(LEFT * 4 + UP * 2, RIGHT * 4 + UP * 2, stroke_width=24).set_stroke( + [BLUE, RED] ) # ── Dashed 2-stop gradient (let Manim pick dash count) ───────────────── - dashed = ( - DashedLine( - LEFT * 4 + DOWN * 2, - RIGHT * 4 + DOWN * 2, - stroke_width=24, - ) - .set_stroke([GREEN, WHITE]) - ) + dashed = DashedLine( + LEFT * 4 + DOWN * 2, + RIGHT * 4 + DOWN * 2, + stroke_width=24, + ).set_stroke([GREEN, WHITE]) self.add(solid, dashed) diff --git a/tests/test_gradient_order.py b/tests/test_gradient_order.py index 297eea60f7..45488a6d91 100644 --- a/tests/test_gradient_order.py +++ b/tests/test_gradient_order.py @@ -1,6 +1,6 @@ import numpy as np -from manim import Line, BLUE, RED +from manim import BLUE, RED, Line from manim.constants import LEFT, RIGHT From 4d9e766047b67c575b32edf1f12a2c28507c6671 Mon Sep 17 00:00:00 2001 From: Bennett Liyanto Date: Wed, 23 Apr 2025 18:20:06 -0400 Subject: [PATCH 4/5] changes --- manim/mobject/types/vectorized_mobject.py | 46 ++-- tests/test_config.py | 301 +++------------------- 2 files changed, 63 insertions(+), 284 deletions(-) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index cb10426b0c..ef7c953e6f 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -247,35 +247,47 @@ def generate_rgbas_array( def update_rgbas_array( self, array_name: str, - color: ManimColor | None = None, - opacity: float | None = None, + color: ManimColor | list[ParsableManimColor] | None = None, + opacity: float | Iterable[float] | None = None, ) -> Self: - rgbas = self.generate_rgbas_array(color, opacity) + if isinstance(color, (list, tuple)) and len(color) > 1: + if opacity is None: + opacities = it.repeat(1.0) + elif isinstance(opacity, (int, float)): + opacities = it.repeat(float(opacity)) + else: + if len(opacity) != len(color): + raise ValueError( + "When passing multiple colours, 'opacity' must be a " + "scalar or have the same length as 'color' " + f"(got {len(opacity)} vs {len(color)})" + ) + opacities = opacity + + rgba_list = [ + ManimColor(c).to_rgba_with_alpha(a) + for c, a in zip(color, opacities) + ] + rgbas = np.asarray(rgba_list, dtype=float) + else: + rgbas = self.generate_rgbas_array(color, opacity) + if not hasattr(self, array_name): setattr(self, array_name, rgbas) return self - # Match up current rgbas array with the newly calculated - # one. 99% of the time they'll be the same. + curr_rgbas = getattr(self, array_name) - if isinstance(color, (list, tuple)) and len(color) > 1: - rgba_list = [ - ManimColor(c).to_rgba_with_alpha( - opacity if opacity is not None else curr_rgbas[0, 3] - ) - for c in color - ] - rgbas = np.array(rgba_list) if len(curr_rgbas) < len(rgbas): curr_rgbas = stretch_array_to_length(curr_rgbas, len(rgbas)) setattr(self, array_name, curr_rgbas) elif len(rgbas) < len(curr_rgbas): rgbas = stretch_array_to_length(rgbas, len(curr_rgbas)) - # Only update rgb if color was not None, and only - # update alpha channel if opacity was passed in - if color is not None: + + if color is not None: curr_rgbas[:, :3] = rgbas[:, :3] - if opacity is not None: + if opacity is not None: curr_rgbas[:, 3] = rgbas[:, 3] + return self def set_fill( diff --git a/tests/test_config.py b/tests/test_config.py index 0703b31bf4..001c9092e4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,267 +1,34 @@ -from __future__ import annotations - -import tempfile -from pathlib import Path - -import numpy as np -import pytest - -from manim import WHITE, Scene, Square, Tex, Text, tempconfig -from manim._config.utils import ManimConfig -from tests.assert_utils import assert_dir_exists, assert_dir_filled, assert_file_exists - - -def test_tempconfig(config): - """Test the tempconfig context manager.""" - original = config.copy() - - with tempconfig({"frame_width": 100, "frame_height": 42}): - # check that config was modified correctly - assert config["frame_width"] == 100 - assert config["frame_height"] == 42 - - # check that no keys are missing and no new keys were added - assert set(original.keys()) == set(config.keys()) - - # check that the keys are still untouched - assert set(original.keys()) == set(config.keys()) - - # check that config is correctly restored - for k, v in original.items(): - if isinstance(v, np.ndarray): - np.testing.assert_allclose(config[k], v) - else: - assert config[k] == v - - -@pytest.mark.parametrize( - ("format", "expected_file_extension"), - [ - ("mp4", ".mp4"), - ("webm", ".webm"), - ("mov", ".mov"), - ("gif", ".mp4"), - ], -) -def test_resolve_file_extensions(config, format, expected_file_extension): - config.format = format - assert config.movie_file_extension == expected_file_extension - - -class MyScene(Scene): - def construct(self): - self.add(Square()) - self.add(Text("Prepare for unforeseen consequencesλ")) - self.add(Tex(r"$\lambda$")) - self.wait(1) - - -def test_transparent(config): - """Test the 'transparent' config option.""" - config.verbosity = "ERROR" - config.dry_run = True - - scene = MyScene() - scene.render() - frame = scene.renderer.get_frame() - np.testing.assert_allclose(frame[0, 0], [0, 0, 0, 255]) - - config.transparent = True - - scene = MyScene() - scene.render() - frame = scene.renderer.get_frame() - np.testing.assert_allclose(frame[0, 0], [0, 0, 0, 0]) - - -def test_transparent_by_background_opacity(config, dry_run): - config.background_opacity = 0.5 - assert config.transparent is True - - scene = MyScene() - scene.render() - frame = scene.renderer.get_frame() - np.testing.assert_allclose(frame[0, 0], [0, 0, 0, 127]) - assert config.movie_file_extension == ".mov" - assert config.transparent is True - - -def test_background_color(config): - """Test the 'background_color' config option.""" - config.background_color = WHITE - config.verbosity = "ERROR" - config.dry_run = True - - scene = MyScene() - scene.render() - frame = scene.renderer.get_frame() - np.testing.assert_allclose(frame[0, 0], [255, 255, 255, 255]) - - -def test_digest_file(tmp_path, config): - """Test that a config file can be digested programmatically.""" - with tempfile.NamedTemporaryFile("w", dir=tmp_path, delete=False) as tmp_cfg: - tmp_cfg.write( - """ - [CLI] - media_dir = this_is_my_favorite_path - video_dir = {media_dir}/videos - sections_dir = {media_dir}/{scene_name}/prepare_for_unforeseen_consequences - frame_height = 10 - """, - ) - config.digest_file(tmp_cfg.name) - - assert config.get_dir("media_dir") == Path("this_is_my_favorite_path") - assert config.get_dir("video_dir") == Path("this_is_my_favorite_path/videos") - assert config.get_dir("sections_dir", scene_name="test") == Path( - "this_is_my_favorite_path/test/prepare_for_unforeseen_consequences" - ) - - -def test_custom_dirs(tmp_path, config): - config.media_dir = tmp_path - config.save_sections = True - config.log_to_file = True - config.frame_rate = 15 - config.pixel_height = 854 - config.pixel_width = 480 - config.sections_dir = "{media_dir}/test_sections" - config.video_dir = "{media_dir}/test_video" - config.partial_movie_dir = "{media_dir}/test_partial_movie_dir" - config.images_dir = "{media_dir}/test_images" - config.text_dir = "{media_dir}/test_text" - config.tex_dir = "{media_dir}/test_tex" - config.log_dir = "{media_dir}/test_log" - - scene = MyScene() - scene.render() - tmp_path = Path(tmp_path) - assert_dir_filled(tmp_path / "test_sections") - assert_file_exists(tmp_path / "test_sections/MyScene.json") - - assert_dir_filled(tmp_path / "test_video") - assert_file_exists(tmp_path / "test_video/MyScene.mp4") - - assert_dir_filled(tmp_path / "test_partial_movie_dir") - assert_file_exists(tmp_path / "test_partial_movie_dir/partial_movie_file_list.txt") - - # TODO: another example with image output would be nice - assert_dir_exists(tmp_path / "test_images") - - assert_dir_filled(tmp_path / "test_text") - assert_dir_filled(tmp_path / "test_tex") - assert_dir_filled(tmp_path / "test_log") - - -def test_pixel_dimensions(tmp_path, config): - with tempfile.NamedTemporaryFile("w", dir=tmp_path, delete=False) as tmp_cfg: - tmp_cfg.write( - """ - [CLI] - pixel_height = 10 - pixel_width = 10 - """, - ) - config.digest_file(tmp_cfg.name) - - # aspect ratio is set using pixel measurements - np.testing.assert_allclose(config.aspect_ratio, 1.0) - # if not specified in the cfg file, frame_width is set using the aspect ratio - np.testing.assert_allclose(config.frame_height, 8.0) - np.testing.assert_allclose(config.frame_width, 8.0) - - -def test_frame_size(tmp_path, config): - """Test that the frame size can be set via config file.""" - np.testing.assert_allclose( - config.aspect_ratio, config.pixel_width / config.pixel_height - ) - np.testing.assert_allclose(config.frame_height, 8.0) - - with tempfile.NamedTemporaryFile("w", dir=tmp_path, delete=False) as tmp_cfg: - tmp_cfg.write( - """ - [CLI] - pixel_height = 10 - pixel_width = 10 - frame_height = 10 - frame_width = 10 - """, - ) - config.digest_file(tmp_cfg.name) - - np.testing.assert_allclose(config.aspect_ratio, 1.0) - # if both are specified in the cfg file, the aspect ratio is ignored - np.testing.assert_allclose(config.frame_height, 10.0) - np.testing.assert_allclose(config.frame_width, 10.0) - - -def test_temporary_dry_run(config): - """Test that tempconfig correctly restores after setting dry_run.""" - assert config["write_to_movie"] - assert not config["save_last_frame"] - - with tempconfig({"dry_run": True}): - assert not config["write_to_movie"] - assert not config["save_last_frame"] - - assert config["write_to_movie"] - assert not config["save_last_frame"] - - -def test_dry_run_with_png_format(config, dry_run): - """Test that there are no exceptions when running a png without output""" - config.write_to_movie = False - config.disable_caching = True - assert config.dry_run is True - scene = MyScene() - scene.render() - - -def test_dry_run_with_png_format_skipped_animations(config, dry_run): - """Test that there are no exceptions when running a png without output and skipped animations""" - config.write_to_movie = False - config.disable_caching = True - assert config["dry_run"] is True - scene = MyScene(skip_animations=True) - scene.render() - - -def test_tex_template_file(tmp_path): - """Test that a custom tex template file can be set from a config file.""" - tex_file = Path(tmp_path / "my_template.tex") - tex_file.write_text("Hello World!") - with tempfile.NamedTemporaryFile("w", dir=tmp_path, delete=False) as tmp_cfg: - tmp_cfg.write( - f""" - [CLI] - tex_template_file = {tex_file} - """, - ) - - custom_config = ManimConfig().digest_file(tmp_cfg.name) - - assert Path(custom_config.tex_template_file) == tex_file - assert custom_config.tex_template.body == "Hello World!" - - -def test_from_to_animations_only_first_animation(config): - config: ManimConfig - config.from_animation_number = 0 - config.upto_animation_number = 0 - - class SceneWithTwoAnimations(Scene): - def construct(self): - self.after_first_animation = False - s = Square() - self.add(s) - self.play(s.animate.scale(2)) - self.renderer.update_skipping_status() - self.after_first_animation = True - self.play(s.animate.scale(2)) - - scene = SceneWithTwoAnimations() - scene.render() - - assert scene.after_first_animation is False +""" +Unit tests for #4198 – gradient order must be stable no matter how the line +is oriented. The test is intentionally **numeric only** (no rendering), so it +runs quickly under pytest-xdist. +""" + +from manim import BLUE, RED, LEFT, RIGHT, Line + + +def _first_last_stroke_colors(vmob): + """Return the first and last stroke colours as ManimColor objects.""" + cols = vmob.get_stroke_colors() + # For a two-stop gradient Manim stores exactly two rows in stroke_rgbas + assert len(cols) >= 2, "gradient expected at least two colour stops" + return cols[0], cols[-1] + + +def test_gradient_order_preserved_forward(): + """Baseline sanity: forward-oriented line keeps [BLUE, RED].""" + line = Line(LEFT, RIGHT).set_color([BLUE, RED]) + first, last = _first_last_stroke_colors(line) + assert first == BLUE + assert last == RED + + +def test_gradient_order_preserved_reversed(): + """ + Reversed anchor order used to flip the gradient; the fix in PR #4227 should + keep the user-specified order intact. + """ + line = Line(RIGHT, LEFT).set_color([BLUE, RED]) + first, last = _first_last_stroke_colors(line) + assert first == BLUE + assert last == RED From 4a59573ea4649dac40e5019e46055101a09b6054 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 22:21:52 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/mobject/types/vectorized_mobject.py | 13 ++++++------- tests/test_config.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index ef7c953e6f..aa09c707ed 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -252,10 +252,10 @@ def update_rgbas_array( ) -> Self: if isinstance(color, (list, tuple)) and len(color) > 1: if opacity is None: - opacities = it.repeat(1.0) + opacities = it.repeat(1.0) elif isinstance(opacity, (int, float)): - opacities = it.repeat(float(opacity)) - else: + opacities = it.repeat(float(opacity)) + else: if len(opacity) != len(color): raise ValueError( "When passing multiple colours, 'opacity' must be a " @@ -265,8 +265,7 @@ def update_rgbas_array( opacities = opacity rgba_list = [ - ManimColor(c).to_rgba_with_alpha(a) - for c, a in zip(color, opacities) + ManimColor(c).to_rgba_with_alpha(a) for c, a in zip(color, opacities) ] rgbas = np.asarray(rgba_list, dtype=float) else: @@ -283,9 +282,9 @@ def update_rgbas_array( elif len(rgbas) < len(curr_rgbas): rgbas = stretch_array_to_length(rgbas, len(curr_rgbas)) - if color is not None: + if color is not None: curr_rgbas[:, :3] = rgbas[:, :3] - if opacity is not None: + if opacity is not None: curr_rgbas[:, 3] = rgbas[:, 3] return self diff --git a/tests/test_config.py b/tests/test_config.py index 001c9092e4..5fe6afe681 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -4,7 +4,7 @@ runs quickly under pytest-xdist. """ -from manim import BLUE, RED, LEFT, RIGHT, Line +from manim import BLUE, LEFT, RED, RIGHT, Line def _first_last_stroke_colors(vmob):