Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions manim/mobject/mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,8 @@ def apply_points_function_about_point(
if about_edge is None:
about_edge = ORIGIN
about_point = self.get_critical_point(about_edge)
# Make a copy to prevent mutation of the original array if about_point is a view
about_point = np.array(about_point, copy=True)
for mob in self.family_members_with_points():
mob.points -= about_point
mob.points = func(mob.points)
Expand Down
77 changes: 76 additions & 1 deletion tests/module/mobject/mobject/test_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np
import pytest

from manim import DL, UR, Circle, Mobject, Rectangle, Square, VGroup
from manim import DL, PI, UR, Circle, Mobject, Rectangle, Square, Triangle, VGroup


def test_mobject_add():
Expand Down Expand Up @@ -168,3 +168,78 @@ def test_mobject_dimensions_has_points_and_children():
assert inner_rect.width == 2
assert inner_rect.height == 1
assert inner_rect.depth == 0


def test_rotate_about_vertex_view():
"""Test that rotating about a vertex obtained from get_vertices() works correctly.

This is a regression test for an issue where get_vertices() returns a view of the points array,
and using it as about_point in rotate() would cause the view to be mutated.
"""
triangle = Triangle()
original_vertices = triangle.get_vertices().copy()
first_vertex = original_vertices[0].copy()

# This should rotate about the first vertex without corrupting it
triangle.rotate(PI / 2, about_point=triangle.get_vertices()[0])

# The first vertex should remain in the same position (within numerical precision)
rotated_vertices = triangle.get_vertices()
np.testing.assert_allclose(rotated_vertices[0], first_vertex, atol=1e-6)


def test_scale_about_vertex_view():
"""Test that scaling about a vertex obtained from get_vertices() works correctly.

This is a regression test for an issue where get_vertices() returns a view of the points array,
and using it as about_point in scale() would cause the view to be mutated.
"""
triangle = Triangle()
original_vertices = triangle.get_vertices().copy()
first_vertex = original_vertices[0].copy()

# This should scale about the first vertex without corrupting it
triangle.scale(2, about_point=triangle.get_vertices()[0])

# The first vertex should remain in the same position (within numerical precision)
scaled_vertices = triangle.get_vertices()
np.testing.assert_allclose(scaled_vertices[0], first_vertex, atol=1e-6)


def test_stretch_about_vertex_view():
"""Test that stretching about a vertex obtained from get_vertices() works correctly.

This is a regression test for an issue where get_vertices() returns a view of the points array,
and using it as about_point in stretch() would cause the view to be mutated.
"""
triangle = Triangle()
original_vertices = triangle.get_vertices().copy()
first_vertex = original_vertices[0].copy()

# This should stretch about the first vertex without corrupting it
triangle.stretch(2, 0, about_point=triangle.get_vertices()[0])

# The first vertex should remain in the same position (within numerical precision)
stretched_vertices = triangle.get_vertices()
np.testing.assert_allclose(stretched_vertices[0], first_vertex, atol=1e-6)


def test_apply_matrix_about_vertex_view():
"""Test that apply_matrix about a vertex obtained from get_vertices() works correctly.

This is a regression test for an issue where get_vertices() returns a view of the points array,
and using it as about_point in apply_matrix() would cause the view to be mutated.
"""
triangle = Triangle()
original_vertices = triangle.get_vertices().copy()
first_vertex = original_vertices[0].copy()

# Define a rotation matrix (90 degrees rotation around z-axis)
rotation_matrix = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])

# This should apply the matrix about the first vertex without corrupting it
triangle.apply_matrix(rotation_matrix, about_point=triangle.get_vertices()[0])

# The first vertex should remain in the same position (within numerical precision)
transformed_vertices = triangle.get_vertices()
np.testing.assert_allclose(transformed_vertices[0], first_vertex, atol=1e-6)
26 changes: 26 additions & 0 deletions tests/opengl/test_opengl_mobject.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import numpy as np
import pytest

from manim import PI
from manim.mobject.opengl.opengl_geometry import OpenGLTriangle
from manim.mobject.opengl.opengl_mobject import OpenGLMobject


Expand Down Expand Up @@ -60,3 +63,26 @@ def test_opengl_mobject_remove(using_opengl_renderer):
assert len(obj.submobjects) == 10

assert obj.remove(OpenGLMobject()) is obj


def test_opengl_rotate_about_vertex_view(using_opengl_renderer):
"""Test that rotating about a vertex obtained from get_vertices() works correctly.

This is a regression test for an issue in the non-OpenGL (Cairo) renderer where
get_vertices() returns a view of the points array, and using it as about_point
in rotate() would cause the view to be mutated. The OpenGL renderer was not affected
by this bug due to its different implementation (using `arr - about_point` which
creates a temporary array rather than `arr -= about_point` which mutates in-place).

This test verifies that the OpenGL renderer continues to handle vertex views correctly.
"""
triangle = OpenGLTriangle()
original_vertices = triangle.get_vertices().copy()
first_vertex = original_vertices[0].copy()

# This should rotate about the first vertex without corrupting it
triangle.rotate(PI / 2, about_point=triangle.get_vertices()[0])

# The first vertex should remain in the same position (within numerical precision)
rotated_vertices = triangle.get_vertices()
np.testing.assert_allclose(rotated_vertices[0], first_vertex, atol=1e-6)
Loading