Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active October 15, 2025 18:10
Show Gist options
  • Save uwezi/570b831a8e2de9ecc666de3836e020a6 to your computer and use it in GitHub Desktop.
Save uwezi/570b831a8e2de9ecc666de3836e020a6 to your computer and use it in GitHub Desktop.
[Line through points] Creates a line through several points. #manim #line #rounded #tip #radius
class LineThroughPoints(Line):
def __init__(self, pointsdirections, radius=0, **kwargs):
super().__init__(**kwargs)
self.pointsdirections = pointsdirections
self.radius = radius
self.set_points([pointsdirections[0][0]])
for p0,p1 in zip(pointsdirections,pointsdirections[1:]):
dx,dy,dz = p1[0]-p0[0]
if len(p0) == 3:
r = p0[2]
else:
r = self.radius
if p0[1]=="-|":
if r>0:
self.add_line_to(p0[0]*UP+p1[0]*RIGHT-np.sign(dx)*r*RIGHT)
self.add_cubic_bezier_curve(
p0[0]*UP+p1[0]*RIGHT-np.sign(dx)*r*RIGHT,
p0[0]*UP+p1[0]*RIGHT,
p0[0]*UP+p1[0]*RIGHT,
p0[0]*UP+np.sign(dy)*r*UP+p1[0]*RIGHT
)
else:
self.add_line_to(p0[0]*UP+p1[0]*RIGHT)
elif p0[1]=="|-":
if radius>0:
self.add_line_to(p0[0]*RIGHT+p1[0]*UP-np.sign(dy)*r*UP)
self.add_cubic_bezier_curve(
p0[0]*RIGHT+p1[0]*UP-np.sign(dy)*r*UP,
p0[0]*RIGHT+p1[0]*UP,
p0[0]*RIGHT+p1[0]*UP,
p0[0]*RIGHT+np.sign(dx)*r*RIGHT+p1[0]*UP
)
else:
self.add_line_to(p0[0]*RIGHT+p1[0]*UP)
self.add_line_to(p1[0])
def add_tip(
line,
shorten=True,
tip_shape: type[tips.ArrowTip] | None = None,
tip_length: float | None = None,
tip_width: float | None = None,
):
tip = line.get_unpositioned_tip(
tip_length=tip_length,
tip_shape = tip_shape,
tip_width = tip_width,
)
tipangle = Line(tip.base, tip.tip_point).get_angle()
angle = Line(line.point_from_proportion(0.999),line.point_from_proportion(1.0)).get_angle()
tip.rotate(angle-tipangle,about_point=tip.base)
tip.shift(line.get_end()-tip.tip_point)
if shorten==True:
points = line.get_all_points()
points[-1]=tip.base
line.set_points(points)
line.add(tip)
return line
class testLineThroughPoints(Scene):
def construct(self):
p1 = Dot([-4,2,0],color=RED)
p2 = Dot([4,-1,0],color=BLUE)
p3 = Dot([6,+3,0],color=YELLOW)
line1 = LineThroughPoints(
[
(p1.get_center(),"|-"),
(p2.get_center(),"|-",1.5),
(p3.get_center(),""),
],
radius=0.5
)
line2 = LineThroughPoints(
[
(p1.get_center(),"-|"),
(p2.get_center(),"-|"),
(p3.get_center(),""),
],
radius=0.5,
color=GREEN
).add_tip()
line3 = LineThroughPoints(
[
(p1.get_center(),""),
(p2.get_center(),""),
(p3.get_center(),""),
],
color=ORANGE
)
self.add(p1,p2,p3)
self.add(line1,line2,line3)

https://discord.com/channels/581738731934056449/1427983312143781938/1427983312143781938

After a discussion on Discord this gist creates a line between several points with optional rectangular direction hints similar to Tikz ("-|" and "|-"), as well as corner radii.

It also corrects for the default behavior of .add_tip() to add a slant to a given angled line object.

image

The syntax should be rather self-explanatory together with the example.

LineThroughPoints([(p1,""),(p2,""),(p3,"")])

creates a line which goes straight from p1 to p2 to p3, optinally with an arrow tip at the end.

LineThroughPoints([(p1,"-|"),(p2,"")])

creates a line which leaves p1 horizontally until it is right above p2, then it drops vertically to p2.

LineThroughPoints([(p1,"|-"),(p2,"")])

creates a line which leaves p1 vertically until it is on the same height as p2, then it goes horizontally to p2.

The directional argument of the last point doesn't matter.

Additional a global radius= can be defined to round the midway corners between the control points, and/or individual radii can be given to each rectangular midway-corner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment