Fix up FillArrow

This commit is contained in:
Grant Sanderson
2024-08-21 15:58:57 -05:00
parent 22ee13a884
commit ceac4fbe1a

View File

@ -738,10 +738,11 @@ class Arrow(Line):
self,
start: Vect3 | Mobject = LEFT,
end: Vect3 | Mobject = LEFT,
buff: float = MED_SMALL_BUFF,
path_arc: float = 0,
fill_color: ManimColor = GREY_A,
fill_opacity: float = 1.0,
stroke_width: float = 0.0,
buff: float = MED_SMALL_BUFF,
thickness: float = 3.0,
tip_width_ratio: float = 5,
tip_angle: float = PI / 3,
@ -760,19 +761,11 @@ class Arrow(Line):
fill_opacity=fill_opacity,
stroke_width=stroke_width,
buff=buff,
path_arc=path_arc,
**kwargs
)
def set_points_by_ends(
self,
start: Vect3,
end: Vect3,
buff: float = 0,
path_arc: float = 0
) -> Self:
# Find the right tip length and thickness
vect = end - start
length = max(get_norm(vect), 1e-8)
def get_key_dimensions(self, length):
width = self.thickness * self.tickness_multiplier
w_ratio = fdiv(self.max_width_to_length_ratio, fdiv(width, length))
if w_ratio < 1:
@ -785,25 +778,48 @@ class Arrow(Line):
tip_length *= t_ratio
tip_width *= t_ratio
# Find points for the stem
return width, tip_width, tip_length
def set_points_by_ends(
self,
start: Vect3,
end: Vect3,
buff: float = 0,
path_arc: float = 0
) -> Self:
vect = end - start
length = max(get_norm(vect), 1e-8) # More systematic min?
unit_vect = normalize(vect)
# Find the right tip length and thickness
width, tip_width, tip_length = self.get_key_dimensions(length - buff)
# Adjust start and end based on buff
if path_arc == 0:
start = start + buff * unit_vect
end = end - buff * unit_vect
else:
R = length / 2 / math.sin(path_arc / 2)
midpoint = 0.5 * (start + end)
center = midpoint + rotate_vector(0.5 * vect, PI / 2) / math.tan(path_arc / 2)
sign = 1
start = center + rotate_vector(start - center, buff / R)
end = center + rotate_vector(end - center, -buff / R)
path_arc -= (2 * buff + tip_length) / R
vect = end - start
length = get_norm(vect)
# Find points for the stem, imagining an arrow pointed to the left
if path_arc == 0:
points1 = (length - tip_length) * np.array([RIGHT, 0.5 * RIGHT, ORIGIN])
points1 += width * UP / 2
points2 = points1[::-1] + width * DOWN
else:
# Solve for radius so that the tip-to-tail length matches |end - start|
a = 2 * (1 - np.cos(path_arc))
b = -2 * tip_length * np.sin(path_arc)
c = tip_length**2 - length**2
R = (-b + np.sqrt(b**2 - 4 * a * c)) / (2 * a)
# Find arc points
points1 = quadratic_bezier_points_for_arc(path_arc)
points2 = np.array(points1[::-1])
points1 *= (R + width / 2)
points2 *= (R - width / 2)
if path_arc < 0:
tip_length *= -1
rot_T = rotation_matrix_transpose(PI / 2 - path_arc, OUT)
for points in points1, points2:
points[:] = np.dot(points, rot_T)
@ -820,10 +836,7 @@ class Arrow(Line):
self.add_subpath(points2)
self.add_line_to(points1[0])
if length > 0 and self.get_length() > 0:
# Final correction
super().scale(length / self.get_length())
# Reposition to match proper start and end
self.rotate(angle_of_vector(vect) - self.get_angle())
self.rotate(
PI / 2 - np.arccos(normalize(vect)[2]),
@ -847,6 +860,9 @@ class Arrow(Line):
def get_end(self) -> Vect3:
return self.get_points()[self.tip_index]
def get_start_and_end(self):
return (self.get_start(), self.get_end())
def put_start_and_end_on(self, start: Vect3, end: Vect3) -> Self:
self.set_points_by_ends(start, end, buff=0, path_arc=self.path_arc)
return self