mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Fix polyline distortion when adding tips to Line paths
This commit is contained in:
parent
21cf9998cc
commit
a41aba21ca
3 changed files with 128 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ from manim.mobject.mobject import Mobject
|
|||
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
||||
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
|
||||
from manim.mobject.types.vectorized_mobject import DashedVMobject, VGroup, VMobject
|
||||
from manim.utils.bezier import partial_bezier_points
|
||||
from manim.utils.color import WHITE
|
||||
from manim.utils.space_ops import angle_of_vector, line_intersection, normalize
|
||||
|
||||
|
|
@ -234,6 +235,49 @@ class Line(TipableVMobject):
|
|||
self.generate_points()
|
||||
return super().put_start_and_end_on(start, end)
|
||||
|
||||
def _trim_path_with_tip_base(
|
||||
self,
|
||||
point: Point3DLike,
|
||||
at_start: bool,
|
||||
) -> bool:
|
||||
if self.path_arc != 0 or self.get_num_curves() <= 1:
|
||||
return False
|
||||
|
||||
curve_index = 0 if at_start else self.get_num_curves() - 1
|
||||
if curve_index < 0:
|
||||
return False
|
||||
|
||||
curve_points = self.get_nth_curve_points(curve_index)
|
||||
start_anchor = curve_points[0]
|
||||
end_anchor = curve_points[-1]
|
||||
segment = end_anchor - start_anchor
|
||||
segment_length_sq = float(np.dot(segment, segment))
|
||||
if segment_length_sq == 0:
|
||||
return False
|
||||
|
||||
residue = float(
|
||||
np.clip(
|
||||
np.dot(np.asarray(point) - start_anchor, segment) / segment_length_sq,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
)
|
||||
nppc = self.n_points_per_curve
|
||||
if at_start:
|
||||
self.points[:nppc] = partial_bezier_points(curve_points, residue, 1)
|
||||
else:
|
||||
self.points[-nppc:] = partial_bezier_points(curve_points, 0, residue)
|
||||
return True
|
||||
|
||||
def reset_endpoints_based_on_tip(self, tip: ArrowTip, at_start: bool) -> Self:
|
||||
if self.get_length() == 0:
|
||||
return self
|
||||
|
||||
if self._trim_path_with_tip_base(tip.base, at_start):
|
||||
return self
|
||||
|
||||
return super().reset_endpoints_based_on_tip(tip, at_start)
|
||||
|
||||
def get_vector(self) -> Vector3D:
|
||||
return self.get_end() - self.get_start()
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ from manim.typing import (
|
|||
Vector3D,
|
||||
Vector3DLike,
|
||||
)
|
||||
from manim.utils.bezier import partial_bezier_points
|
||||
from manim.utils.color import *
|
||||
from manim.utils.iterables import adjacent_n_tuples, adjacent_pairs
|
||||
from manim.utils.simple_functions import clip
|
||||
|
|
@ -553,6 +554,45 @@ class OpenGLLine(OpenGLTipableVMobject):
|
|||
self.set_points_by_ends(start, end, self.path_arc)
|
||||
return super().put_start_and_end_on(start, end)
|
||||
|
||||
def _trim_path_with_tip_base(self, point: Point3DLike, at_start: bool) -> bool:
|
||||
if self.path_arc != 0 or self.get_num_curves() <= 1:
|
||||
return False
|
||||
|
||||
curve_index = 0 if at_start else self.get_num_curves() - 1
|
||||
if curve_index < 0:
|
||||
return False
|
||||
|
||||
curve_points = self.get_nth_curve_points(curve_index)
|
||||
start_anchor = curve_points[0]
|
||||
end_anchor = curve_points[-1]
|
||||
segment = end_anchor - start_anchor
|
||||
segment_length_sq = float(np.dot(segment, segment))
|
||||
if segment_length_sq == 0:
|
||||
return False
|
||||
|
||||
residue = float(
|
||||
np.clip(
|
||||
np.dot(np.asarray(point) - start_anchor, segment) / segment_length_sq,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
)
|
||||
nppc = self.n_points_per_curve
|
||||
if at_start:
|
||||
self.points[:nppc] = partial_bezier_points(curve_points, residue, 1)
|
||||
else:
|
||||
self.points[-nppc:] = partial_bezier_points(curve_points, 0, residue)
|
||||
return True
|
||||
|
||||
def reset_endpoints_based_on_tip(self, tip: OpenGLArrowTip, at_start: bool) -> Self:
|
||||
if self.get_length() == 0:
|
||||
return self
|
||||
|
||||
if self._trim_path_with_tip_base(tip.get_base(), at_start):
|
||||
return self
|
||||
|
||||
return super().reset_endpoints_based_on_tip(tip, at_start)
|
||||
|
||||
def get_vector(self) -> Vector3D:
|
||||
return self.get_end() - self.get_start()
|
||||
|
||||
|
|
|
|||
|
|
@ -217,6 +217,50 @@ def test_line_with_buff_and_path_arc():
|
|||
np.testing.assert_allclose(line.points, expected_points)
|
||||
|
||||
|
||||
def test_add_tip_preserves_polyline_corner():
|
||||
line = Line()
|
||||
line.set_points_as_corners(
|
||||
[
|
||||
np.array([0.0, 0.0, 0.0]),
|
||||
np.array([0.0, 2.0, 0.0]),
|
||||
np.array([3.0, 2.0, 0.0]),
|
||||
]
|
||||
)
|
||||
original_first_curve = line.points[:4].copy()
|
||||
|
||||
line.add_tip(tip_length=0.5)
|
||||
|
||||
np.testing.assert_allclose(line.points[:4], original_first_curve)
|
||||
np.testing.assert_allclose(line.points[3], np.array([0.0, 2.0, 0.0]))
|
||||
np.testing.assert_allclose(line.points[4], np.array([0.0, 2.0, 0.0]))
|
||||
np.testing.assert_allclose(line.points[-1], line.tip.base)
|
||||
np.testing.assert_allclose(line.tip.base, np.array([2.5, 2.0, 0.0]))
|
||||
|
||||
|
||||
def test_add_start_tip_preserves_polyline_corner():
|
||||
line = Line()
|
||||
line.set_points_as_corners(
|
||||
[
|
||||
np.array([0.0, 0.0, 0.0]),
|
||||
np.array([0.0, 2.0, 0.0]),
|
||||
np.array([3.0, 2.0, 0.0]),
|
||||
]
|
||||
)
|
||||
original_last_curve = line.points[-4:].copy()
|
||||
|
||||
line.add_tip(at_start=True, tip_length=0.5)
|
||||
|
||||
np.testing.assert_allclose(line.points[-4:], original_last_curve)
|
||||
np.testing.assert_allclose(line.points[3], np.array([0.0, 2.0, 0.0]))
|
||||
np.testing.assert_allclose(line.points[4], np.array([0.0, 2.0, 0.0]))
|
||||
np.testing.assert_allclose(line.points[0], line.start_tip.base, atol=1e-12)
|
||||
np.testing.assert_allclose(
|
||||
line.start_tip.base,
|
||||
np.array([0.0, 0.5, 0.0]),
|
||||
atol=1e-12,
|
||||
)
|
||||
|
||||
|
||||
def test_Circle_point_at_angle():
|
||||
from manim import TAU
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue