mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Port geometry.py up to DashedLine
This commit is contained in:
parent
53b2d160c2
commit
4246483978
9 changed files with 619 additions and 499 deletions
|
|
@ -18,6 +18,26 @@ if config["use_opengl_renderer"]:
|
|||
# for a 1920x1080 video)
|
||||
|
||||
|
||||
class Testing(Scene):
|
||||
def construct(self):
|
||||
c = OpenGLLine(LEFT, RIGHT)
|
||||
c2 = OpenGLLine(LEFT, RIGHT)
|
||||
print(c.data["points"])
|
||||
c2.shift(RIGHT)
|
||||
self.play(Transform(c, c2))
|
||||
|
||||
# c = OpenGLDashedLine(LEFT, RIGHT)
|
||||
# c2 = OpenGLDashedLine(LEFT, RIGHT)
|
||||
# print(c.data["points"])
|
||||
# c2.shift(RIGHT)
|
||||
# self.play(Transform(c, c2))
|
||||
|
||||
# mob = ArcBetweenPoints(LEFT, RIGHT)
|
||||
# mob2 = ArcBetweenPoints(LEFT, RIGHT)
|
||||
# mob2.shift(RIGHT)
|
||||
# self.play(Transform(mob, mob2), run_time=0.3)
|
||||
|
||||
|
||||
class OpeningManim(Scene):
|
||||
def construct(self):
|
||||
title = Tex(r"This is some \LaTeX")
|
||||
|
|
@ -75,20 +95,15 @@ class OpeningManim(Scene):
|
|||
|
||||
class SquareToCircle(Scene):
|
||||
def construct(self):
|
||||
# circle = Circle()
|
||||
# square = Square()
|
||||
# square.flip(RIGHT)
|
||||
# square.rotate(-3 * TAU / 8)
|
||||
# circle.set_fill(PINK, opacity=0.5)
|
||||
circle = Circle()
|
||||
square = Square()
|
||||
square.flip(RIGHT)
|
||||
square.rotate(-3 * TAU / 8)
|
||||
circle.set_fill(PINK, opacity=0.5)
|
||||
|
||||
# self.play(ShowCreation(square))
|
||||
# self.play(Transform(square, circle))
|
||||
# self.play(FadeOut(square))
|
||||
|
||||
mob = ArcBetweenPoints(LEFT, RIGHT)
|
||||
mob2 = ArcBetweenPoints(LEFT, RIGHT)
|
||||
mob2.shift(RIGHT)
|
||||
self.play(Transform(mob, mob2))
|
||||
self.play(ShowCreation(square))
|
||||
self.play(Transform(square, circle))
|
||||
self.play(FadeOut(square))
|
||||
|
||||
|
||||
class WarpSquare(Scene):
|
||||
|
|
|
|||
|
|
@ -320,244 +320,277 @@ class OpenGLCircle(OpenGLArc):
|
|||
return self.point_from_proportion((angle - start_angle) / TAU)
|
||||
|
||||
|
||||
# class Dot(Circle):
|
||||
# CONFIG = {
|
||||
# "radius": DEFAULT_DOT_RADIUS,
|
||||
# "stroke_width": 0,
|
||||
# "fill_opacity": 1.0,
|
||||
# "color": WHITE,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, point=ORIGIN, **kwargs):
|
||||
# super().__init__(arc_center=point, **kwargs)
|
||||
#
|
||||
#
|
||||
# class SmallDot(Dot):
|
||||
# CONFIG = {
|
||||
# "radius": DEFAULT_SMALL_DOT_RADIUS,
|
||||
# }
|
||||
#
|
||||
#
|
||||
# class Ellipse(Circle):
|
||||
# CONFIG = {"width": 2, "height": 1}
|
||||
#
|
||||
# def __init__(self, **kwargs):
|
||||
# super().__init__(**kwargs)
|
||||
# self.set_width(self.width, stretch=True)
|
||||
# self.set_height(self.height, stretch=True)
|
||||
#
|
||||
#
|
||||
# class AnnularSector(Arc):
|
||||
# CONFIG = {
|
||||
# "inner_radius": 1,
|
||||
# "outer_radius": 2,
|
||||
# "angle": TAU / 4,
|
||||
# "start_angle": 0,
|
||||
# "fill_opacity": 1,
|
||||
# "stroke_width": 0,
|
||||
# "color": WHITE,
|
||||
# }
|
||||
#
|
||||
# def init_points(self):
|
||||
# inner_arc, outer_arc = [
|
||||
# Arc(
|
||||
# start_angle=self.start_angle,
|
||||
# angle=self.angle,
|
||||
# radius=radius,
|
||||
# arc_center=self.arc_center,
|
||||
# )
|
||||
# for radius in (self.inner_radius, self.outer_radius)
|
||||
# ]
|
||||
# outer_arc.reverse_points()
|
||||
# self.append_points(inner_arc.get_points())
|
||||
# self.add_line_to(outer_arc.get_points()[0])
|
||||
# self.append_points(outer_arc.get_points())
|
||||
# self.add_line_to(inner_arc.get_points()[0])
|
||||
#
|
||||
#
|
||||
# class Sector(AnnularSector):
|
||||
# CONFIG = {"outer_radius": 1, "inner_radius": 0}
|
||||
#
|
||||
#
|
||||
# class Annulus(Circle):
|
||||
# CONFIG = {
|
||||
# "inner_radius": 1,
|
||||
# "outer_radius": 2,
|
||||
# "fill_opacity": 1,
|
||||
# "stroke_width": 0,
|
||||
# "color": WHITE,
|
||||
# "mark_paths_closed": False,
|
||||
# }
|
||||
#
|
||||
# def init_points(self):
|
||||
# self.radius = self.outer_radius
|
||||
# outer_circle = Circle(radius=self.outer_radius)
|
||||
# inner_circle = Circle(radius=self.inner_radius)
|
||||
# inner_circle.reverse_points()
|
||||
# self.append_points(outer_circle.get_points())
|
||||
# self.append_points(inner_circle.get_points())
|
||||
# self.shift(self.arc_center)
|
||||
#
|
||||
#
|
||||
# class Line(TipableVMobject):
|
||||
# CONFIG = {
|
||||
# "buff": 0,
|
||||
# # Angle of arc specified here
|
||||
# "path_arc": 0,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, start=LEFT, end=RIGHT, **kwargs):
|
||||
# digest_config(self, kwargs)
|
||||
# self.set_start_and_end_attrs(start, end)
|
||||
# super().__init__(**kwargs)
|
||||
#
|
||||
# def init_points(self):
|
||||
# self.set_points_by_ends(self.start, self.end, self.buff, self.path_arc)
|
||||
#
|
||||
# def set_points_by_ends(self, start, end, buff=0, path_arc=0):
|
||||
# if path_arc:
|
||||
# self.set_points(Arc.create_quadratic_bezier_points(path_arc))
|
||||
# self.put_start_and_end_on(start, end)
|
||||
# else:
|
||||
# self.set_points_as_corners([start, end])
|
||||
# self.account_for_buff(self.buff)
|
||||
#
|
||||
# def set_path_arc(self, new_value):
|
||||
# self.path_arc = new_value
|
||||
# self.init_points()
|
||||
#
|
||||
# def account_for_buff(self, buff):
|
||||
# if buff == 0:
|
||||
# return
|
||||
# #
|
||||
# if self.path_arc == 0:
|
||||
# length = self.get_length()
|
||||
# else:
|
||||
# length = self.get_arc_length()
|
||||
# #
|
||||
# if length < 2 * buff:
|
||||
# return
|
||||
# buff_prop = buff / length
|
||||
# self.pointwise_become_partial(self, buff_prop, 1 - buff_prop)
|
||||
# return self
|
||||
#
|
||||
# def set_start_and_end_attrs(self, start, end):
|
||||
# # If either start or end are Mobjects, this
|
||||
# # gives their centers
|
||||
# rough_start = self.pointify(start)
|
||||
# rough_end = self.pointify(end)
|
||||
# vect = normalize(rough_end - rough_start)
|
||||
# # Now that we know the direction between them,
|
||||
# # we can find the appropriate boundary point from
|
||||
# # start and end, if they're mobjects
|
||||
# self.start = self.pointify(start, vect) + self.buff * vect
|
||||
# self.end = self.pointify(end, -vect) - self.buff * vect
|
||||
#
|
||||
# def pointify(self, mob_or_point, direction=None):
|
||||
# """
|
||||
# Take an argument passed into Line (or subclass) and turn
|
||||
# it into a 3d point.
|
||||
# """
|
||||
# if isinstance(mob_or_point, Mobject):
|
||||
# mob = mob_or_point
|
||||
# if direction is None:
|
||||
# return mob.get_center()
|
||||
# else:
|
||||
# return mob.get_continuous_bounding_box_point(direction)
|
||||
# else:
|
||||
# point = mob_or_point
|
||||
# result = np.zeros(self.dim)
|
||||
# result[: len(point)] = point
|
||||
# return result
|
||||
#
|
||||
# def put_start_and_end_on(self, start, end):
|
||||
# curr_start, curr_end = self.get_start_and_end()
|
||||
# if (curr_start == curr_end).all():
|
||||
# self.set_points_by_ends(start, end, self.path_arc)
|
||||
# return super().put_start_and_end_on(start, end)
|
||||
#
|
||||
# def get_vector(self):
|
||||
# return self.get_end() - self.get_start()
|
||||
#
|
||||
# def get_unit_vector(self):
|
||||
# return normalize(self.get_vector())
|
||||
#
|
||||
# def get_angle(self):
|
||||
# return angle_of_vector(self.get_vector())
|
||||
#
|
||||
# def get_projection(self, point):
|
||||
# """
|
||||
# Return projection of a point onto the line
|
||||
# """
|
||||
# unit_vect = self.get_unit_vector()
|
||||
# start = self.get_start()
|
||||
# return start + np.dot(point - start, unit_vect) * unit_vect
|
||||
#
|
||||
# def get_slope(self):
|
||||
# return np.tan(self.get_angle())
|
||||
#
|
||||
# def set_angle(self, angle, about_point=None):
|
||||
# if about_point is None:
|
||||
# about_point = self.get_start()
|
||||
# self.rotate(
|
||||
# angle - self.get_angle(),
|
||||
# about_point=about_point,
|
||||
# )
|
||||
# return self
|
||||
#
|
||||
# def set_length(self, length):
|
||||
# self.scale(length / self.get_length())
|
||||
#
|
||||
#
|
||||
# class DashedLine(Line):
|
||||
# CONFIG = {
|
||||
# "dash_length": DEFAULT_DASH_LENGTH,
|
||||
# "dash_spacing": None,
|
||||
# "positive_space_ratio": 0.5,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(*args, **kwargs)
|
||||
# ps_ratio = self.positive_space_ratio
|
||||
# num_dashes = self.calculate_num_dashes(ps_ratio)
|
||||
# dashes = DashedVMobject(
|
||||
# self, num_dashes=num_dashes, positive_space_ratio=ps_ratio
|
||||
# )
|
||||
# self.clear_points()
|
||||
# self.add(*dashes)
|
||||
#
|
||||
# def calculate_num_dashes(self, positive_space_ratio):
|
||||
# try:
|
||||
# full_length = self.dash_length / positive_space_ratio
|
||||
# return int(np.ceil(self.get_length() / full_length))
|
||||
# except ZeroDivisionError:
|
||||
# return 1
|
||||
#
|
||||
# def calculate_positive_space_ratio(self):
|
||||
# return fdiv(
|
||||
# self.dash_length,
|
||||
# self.dash_length + self.dash_spacing,
|
||||
# )
|
||||
#
|
||||
# def get_start(self):
|
||||
# if len(self.submobjects) > 0:
|
||||
# return self.submobjects[0].get_start()
|
||||
# else:
|
||||
# return Line.get_start(self)
|
||||
#
|
||||
# def get_end(self):
|
||||
# if len(self.submobjects) > 0:
|
||||
# return self.submobjects[-1].get_end()
|
||||
# else:
|
||||
# return Line.get_end(self)
|
||||
#
|
||||
# def get_first_handle(self):
|
||||
# return self.submobjects[0].get_points()[1]
|
||||
#
|
||||
# def get_last_handle(self):
|
||||
# return self.submobjects[-1].get_points()[-2]
|
||||
#
|
||||
#
|
||||
class OpenGLDot(OpenGLCircle):
|
||||
def __init__(
|
||||
self,
|
||||
point=ORIGIN,
|
||||
radius=DEFAULT_DOT_RADIUS,
|
||||
stroke_width=0,
|
||||
fill_opacity=1.0,
|
||||
color=WHITE,
|
||||
**kwargs
|
||||
):
|
||||
super().__init__(
|
||||
arc_center=point,
|
||||
radius=radius,
|
||||
stroke_width=stroke_width,
|
||||
fill_opacity=fill_opacity,
|
||||
color=color,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class OpenGLSmallDot(OpenGLDot):
|
||||
def __init__(self, radius=DEFAULT_SMALL_DOT_RADIUS, **kwargs):
|
||||
super().__init__(radius=radius, **kwargs)
|
||||
|
||||
|
||||
class OpenGLEllipse(OpenGLCircle):
|
||||
def __init__(self, width=2, height=1, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_width(width, stretch=True)
|
||||
self.set_height(height, stretch=True)
|
||||
|
||||
|
||||
class OpenGLAnnularSector(OpenGLArc):
|
||||
def __init__(
|
||||
self,
|
||||
inner_radius=1,
|
||||
outer_radius=2,
|
||||
angle=TAU / 4,
|
||||
start_angle=0,
|
||||
fill_opacity=1,
|
||||
stroke_width=0,
|
||||
color=WHITE,
|
||||
**kwargs
|
||||
):
|
||||
self.inner_radius = inner_radius
|
||||
self.outer_radius = outer_radius
|
||||
OpenGLArc.__init__(
|
||||
self,
|
||||
start_angle=start_angle,
|
||||
angle=angle,
|
||||
fill_opacity=fill_opacity,
|
||||
stroke_width=stroke_width,
|
||||
color=color,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def init_points(self):
|
||||
inner_arc, outer_arc = [
|
||||
OpenGLArc(
|
||||
start_angle=self.start_angle,
|
||||
angle=self.angle,
|
||||
radius=radius,
|
||||
arc_center=self.arc_center,
|
||||
)
|
||||
for radius in (self.inner_radius, self.outer_radius)
|
||||
]
|
||||
outer_arc.reverse_points()
|
||||
self.append_points(inner_arc.get_points())
|
||||
self.add_line_to(outer_arc.get_points()[0])
|
||||
self.append_points(outer_arc.get_points())
|
||||
self.add_line_to(inner_arc.get_points()[0])
|
||||
|
||||
|
||||
class OpenGLSector(OpenGLAnnularSector):
|
||||
def __init__(self, outer_radius=1, inner_radius=0, **kwargs):
|
||||
OpenGLAnnularSector.__init__(
|
||||
self, inner_radius=inner_radius, outer_radius=outer_radius, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class OpenGLAnnulus(OpenGLCircle):
|
||||
def __init__(
|
||||
self,
|
||||
inner_radius=1,
|
||||
outer_radius=2,
|
||||
fill_opacity=1,
|
||||
stroke_width=0,
|
||||
color=WHITE,
|
||||
mark_paths_closed=False,
|
||||
**kwargs
|
||||
):
|
||||
self.mark_paths_closed = mark_paths_closed # is this even used?
|
||||
self.inner_radius = inner_radius
|
||||
self.outer_radius = outer_radius
|
||||
OpenGLCircle.__init__(
|
||||
self,
|
||||
fill_opacity=fill_opacity,
|
||||
stroke_width=stroke_width,
|
||||
color=color,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def init_points(self):
|
||||
self.radius = self.outer_radius
|
||||
outer_circle = OpenGLCircle(radius=self.outer_radius)
|
||||
inner_circle = OpenGLCircle(radius=self.inner_radius)
|
||||
inner_circle.reverse_points()
|
||||
self.append_points(outer_circle.get_points())
|
||||
self.append_points(inner_circle.get_points())
|
||||
self.shift(self.arc_center)
|
||||
|
||||
|
||||
class OpenGLLine(OpenGLTipableVMobject):
|
||||
def __init__(self, start=LEFT, end=RIGHT, buff=0, path_arc=0, **kwargs):
|
||||
self.dim = 3
|
||||
self.buff = buff
|
||||
self.path_arc = path_arc
|
||||
self.set_start_and_end_attrs(start, end)
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def init_points(self):
|
||||
self.set_points_by_ends(self.start, self.end, self.buff, self.path_arc)
|
||||
|
||||
def set_points_by_ends(self, start, end, buff=0, path_arc=0):
|
||||
if path_arc:
|
||||
self.set_points(OpenGLArc.create_quadratic_bezier_points(path_arc))
|
||||
self.put_start_and_end_on(start, end)
|
||||
else:
|
||||
self.set_points_as_corners([start, end])
|
||||
self.account_for_buff(self.buff)
|
||||
|
||||
def set_path_arc(self, new_value):
|
||||
self.path_arc = new_value
|
||||
self.init_points()
|
||||
|
||||
def account_for_buff(self, buff):
|
||||
if buff == 0:
|
||||
return
|
||||
#
|
||||
if self.path_arc == 0:
|
||||
length = self.get_length()
|
||||
else:
|
||||
length = self.get_arc_length()
|
||||
#
|
||||
if length < 2 * buff:
|
||||
return
|
||||
buff_prop = buff / length
|
||||
self.pointwise_become_partial(self, buff_prop, 1 - buff_prop)
|
||||
return self
|
||||
|
||||
def set_start_and_end_attrs(self, start, end):
|
||||
# If either start or end are Mobjects, this
|
||||
# gives their centers
|
||||
rough_start = self.pointify(start)
|
||||
rough_end = self.pointify(end)
|
||||
vect = normalize(rough_end - rough_start)
|
||||
# Now that we know the direction between them,
|
||||
# we can find the appropriate boundary point from
|
||||
# start and end, if they're mobjects
|
||||
self.start = self.pointify(start, vect) + self.buff * vect
|
||||
self.end = self.pointify(end, -vect) - self.buff * vect
|
||||
|
||||
def pointify(self, mob_or_point, direction=None):
|
||||
"""
|
||||
Take an argument passed into Line (or subclass) and turn
|
||||
it into a 3d point.
|
||||
"""
|
||||
if isinstance(mob_or_point, Mobject):
|
||||
mob = mob_or_point
|
||||
if direction is None:
|
||||
return mob.get_center()
|
||||
else:
|
||||
return mob.get_continuous_bounding_box_point(direction)
|
||||
else:
|
||||
point = mob_or_point
|
||||
result = np.zeros(self.dim)
|
||||
result[: len(point)] = point
|
||||
return result
|
||||
|
||||
def put_start_and_end_on(self, start, end):
|
||||
curr_start, curr_end = self.get_start_and_end()
|
||||
if (curr_start == curr_end).all():
|
||||
self.set_points_by_ends(start, end, self.path_arc)
|
||||
return super().put_start_and_end_on(start, end)
|
||||
|
||||
def get_vector(self):
|
||||
return self.get_end() - self.get_start()
|
||||
|
||||
def get_unit_vector(self):
|
||||
return normalize(self.get_vector())
|
||||
|
||||
def get_angle(self):
|
||||
return angle_of_vector(self.get_vector())
|
||||
|
||||
def get_projection(self, point):
|
||||
"""
|
||||
Return projection of a point onto the line
|
||||
"""
|
||||
unit_vect = self.get_unit_vector()
|
||||
start = self.get_start()
|
||||
return start + np.dot(point - start, unit_vect) * unit_vect
|
||||
|
||||
def get_slope(self):
|
||||
return np.tan(self.get_angle())
|
||||
|
||||
def set_angle(self, angle, about_point=None):
|
||||
if about_point is None:
|
||||
about_point = self.get_start()
|
||||
self.rotate(
|
||||
angle - self.get_angle(),
|
||||
about_point=about_point,
|
||||
)
|
||||
return self
|
||||
|
||||
def set_length(self, length):
|
||||
self.scale(length / self.get_length())
|
||||
|
||||
|
||||
class OpenGLDashedLine(OpenGLLine):
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
dash_length=DEFAULT_DASH_LENGTH,
|
||||
dash_spacing=None,
|
||||
positive_space_ratio=0.5,
|
||||
**kwargs
|
||||
):
|
||||
self.dash_length = dash_length
|
||||
self.dash_spacing = (dash_spacing,)
|
||||
self.positive_space_ratio = positive_space_ratio
|
||||
super().__init__(*args, **kwargs)
|
||||
ps_ratio = self.positive_space_ratio
|
||||
num_dashes = self.calculate_num_dashes(ps_ratio)
|
||||
dashes = OpenGLDashedVMobject(
|
||||
self, num_dashes=num_dashes, positive_space_ratio=ps_ratio
|
||||
)
|
||||
self.clear_points()
|
||||
self.add(*dashes)
|
||||
|
||||
def calculate_num_dashes(self, positive_space_ratio):
|
||||
try:
|
||||
full_length = self.dash_length / positive_space_ratio
|
||||
return int(np.ceil(self.get_length() / full_length))
|
||||
except ZeroDivisionError:
|
||||
return 1
|
||||
|
||||
def calculate_positive_space_ratio(self):
|
||||
return fdiv(
|
||||
self.dash_length,
|
||||
self.dash_length + self.dash_spacing,
|
||||
)
|
||||
|
||||
def get_start(self):
|
||||
if len(self.submobjects) > 0:
|
||||
return self.submobjects[0].get_start()
|
||||
else:
|
||||
return OpenGLLine.get_start(self)
|
||||
|
||||
def get_end(self):
|
||||
if len(self.submobjects) > 0:
|
||||
return self.submobjects[-1].get_end()
|
||||
else:
|
||||
return OpenGLLine.get_end(self)
|
||||
|
||||
def get_first_handle(self):
|
||||
return self.submobjects[0].get_points()[1]
|
||||
|
||||
def get_last_handle(self):
|
||||
return self.submobjects[-1].get_points()[-2]
|
||||
|
||||
|
||||
# class TangentLine(Line):
|
||||
# CONFIG = {"length": 1, "d_alpha": 1e-6}
|
||||
#
|
||||
|
|
@ -716,253 +749,137 @@ class OpenGLCircle(OpenGLArc):
|
|||
# self.add_cubic_bezier_curve(a0, h0, h1, a1)
|
||||
#
|
||||
#
|
||||
# class Polygon(VMobject):
|
||||
# def __init__(self, *vertices, **kwargs):
|
||||
# self.vertices = vertices
|
||||
# super().__init__(**kwargs)
|
||||
#
|
||||
# def init_points(self):
|
||||
# verts = self.vertices
|
||||
# self.set_points_as_corners([*verts, verts[0]])
|
||||
#
|
||||
# def get_vertices(self):
|
||||
# return self.get_start_anchors()
|
||||
#
|
||||
# def round_corners(self, radius=0.5):
|
||||
# vertices = self.get_vertices()
|
||||
# arcs = []
|
||||
# for v1, v2, v3 in adjacent_n_tuples(vertices, 3):
|
||||
# vect1 = v2 - v1
|
||||
# vect2 = v3 - v2
|
||||
# unit_vect1 = normalize(vect1)
|
||||
# unit_vect2 = normalize(vect2)
|
||||
# angle = angle_between_vectors(vect1, vect2)
|
||||
# # Negative radius gives concave curves
|
||||
# angle *= np.sign(radius)
|
||||
# # Distance between vertex and start of the arc
|
||||
# cut_off_length = radius * np.tan(angle / 2)
|
||||
# # Determines counterclockwise vs. clockwise
|
||||
# sign = np.sign(np.cross(vect1, vect2)[2])
|
||||
# arc = ArcBetweenPoints(
|
||||
# v2 - unit_vect1 * cut_off_length,
|
||||
# v2 + unit_vect2 * cut_off_length,
|
||||
# angle=sign * angle,
|
||||
# n_components=2,
|
||||
# )
|
||||
# arcs.append(arc)
|
||||
#
|
||||
# self.clear_points()
|
||||
# # To ensure that we loop through starting with last
|
||||
# arcs = [arcs[-1], *arcs[:-1]]
|
||||
# for arc1, arc2 in adjacent_pairs(arcs):
|
||||
# self.append_points(arc1.get_points())
|
||||
# line = Line(arc1.get_end(), arc2.get_start())
|
||||
# # Make sure anchors are evenly distributed
|
||||
# len_ratio = line.get_length() / arc1.get_arc_length()
|
||||
# line.insert_n_curves(int(arc1.get_num_curves() * len_ratio))
|
||||
# self.append_points(line.get_points())
|
||||
# return self
|
||||
#
|
||||
#
|
||||
# class RegularPolygon(Polygon):
|
||||
# CONFIG = {
|
||||
# "start_angle": None,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, n=6, **kwargs):
|
||||
# digest_config(self, kwargs, locals())
|
||||
# if self.start_angle is None:
|
||||
# # 0 for odd, 90 for even
|
||||
# self.start_angle = (n % 2) * 90 * DEGREES
|
||||
# start_vect = rotate_vector(RIGHT, self.start_angle)
|
||||
# vertices = compass_directions(n, start_vect)
|
||||
# super().__init__(*vertices, **kwargs)
|
||||
#
|
||||
#
|
||||
# class Triangle(RegularPolygon):
|
||||
# def __init__(self, **kwargs):
|
||||
# super().__init__(n=3, **kwargs)
|
||||
#
|
||||
#
|
||||
# class ArrowTip(Triangle):
|
||||
# CONFIG = {
|
||||
# "fill_opacity": 1,
|
||||
# "fill_color": WHITE,
|
||||
# "stroke_width": 0,
|
||||
# "width": DEFAULT_ARROW_TIP_WIDTH,
|
||||
# "length": DEFAULT_ARROW_TIP_LENGTH,
|
||||
# "angle": 0,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, **kwargs):
|
||||
# Triangle.__init__(self, start_angle=0, **kwargs)
|
||||
# self.set_height(self.width)
|
||||
# self.set_width(self.length, stretch=True)
|
||||
# self.rotate(self.angle)
|
||||
#
|
||||
# def get_base(self):
|
||||
# return self.point_from_proportion(0.5)
|
||||
#
|
||||
# def get_tip_point(self):
|
||||
# return self.get_points()[0]
|
||||
#
|
||||
# def get_vector(self):
|
||||
# return self.get_tip_point() - self.get_base()
|
||||
#
|
||||
# def get_angle(self):
|
||||
# return angle_of_vector(self.get_vector())
|
||||
#
|
||||
# def get_length(self):
|
||||
# return get_norm(self.get_vector())
|
||||
#
|
||||
#
|
||||
# class Rectangle(Polygon):
|
||||
# CONFIG = {
|
||||
# "color": WHITE,
|
||||
# "width": 4.0,
|
||||
# "height": 2.0,
|
||||
# "mark_paths_closed": True,
|
||||
# "close_new_points": True,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, width=None, height=None, **kwargs):
|
||||
# Polygon.__init__(self, UR, UL, DL, DR, **kwargs)
|
||||
#
|
||||
# if width is None:
|
||||
# width = self.width
|
||||
# if height is None:
|
||||
# height = self.height
|
||||
#
|
||||
# self.set_width(width, stretch=True)
|
||||
# self.set_height(height, stretch=True)
|
||||
#
|
||||
#
|
||||
# class Square(Rectangle):
|
||||
# CONFIG = {
|
||||
# "side_length": 2.0,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, side_length=None, **kwargs):
|
||||
# digest_config(self, kwargs)
|
||||
#
|
||||
# if side_length is None:
|
||||
# side_length = self.side_length
|
||||
#
|
||||
# super().__init__(side_length, side_length, **kwargs)
|
||||
#
|
||||
#
|
||||
# class RoundedRectangle(Rectangle):
|
||||
# CONFIG = {
|
||||
# "corner_radius": 0.5,
|
||||
# }
|
||||
#
|
||||
# def __init__(self, **kwargs):
|
||||
# Rectangle.__init__(self, **kwargs)
|
||||
# self.round_corners(self.corner_radius)
|
||||
class OpenGLPolygon(OpenGLVMobject):
|
||||
def __init__(self, *vertices, **kwargs):
|
||||
self.vertices = vertices
|
||||
super().__init__(**kwargs)
|
||||
|
||||
from ..utils.space_ops import angle_of_vector
|
||||
from ..utils.space_ops import angle_between_vectors
|
||||
from ..utils.space_ops import compass_directions
|
||||
from ..utils.space_ops import find_intersection
|
||||
from ..utils.space_ops import get_norm
|
||||
from ..utils.space_ops import normalize
|
||||
from ..utils.space_ops import rotate_vector
|
||||
from ..utils.space_ops import rotation_matrix_transpose
|
||||
from ..utils.iterables import adjacent_n_tuples
|
||||
from ..constants import *
|
||||
from ..mobject.geometry import TipableVMobject
|
||||
from ..mobject.types.vectorized_mobject import VMobject
|
||||
def init_points(self):
|
||||
verts = self.vertices
|
||||
self.set_points_as_corners([*verts, verts[0]])
|
||||
|
||||
def get_vertices(self):
|
||||
return self.get_start_anchors()
|
||||
|
||||
class TestOpenGLArc(TipableVMobject):
|
||||
"""A circular arc."""
|
||||
|
||||
opengl_compatible = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
start_angle=0,
|
||||
angle=TAU / 4,
|
||||
radius=1.0,
|
||||
num_components=8,
|
||||
anchors_span_full_range=True,
|
||||
arc_center=ORIGIN,
|
||||
**kwargs
|
||||
):
|
||||
if radius is None: # apparently None is passed by ArcBetweenPoints
|
||||
radius = 1.0
|
||||
self.radius = radius
|
||||
self.num_components = num_components
|
||||
self.anchors_span_full_range = anchors_span_full_range
|
||||
self.arc_center = arc_center
|
||||
self.start_angle = start_angle
|
||||
self.angle = angle
|
||||
self._failed_to_get_center = False
|
||||
TipableVMobject.__init__(self, **kwargs)
|
||||
|
||||
def init_gl_points(self):
|
||||
self.set_points(
|
||||
TestOpenGLArc.create_quadratic_bezier_points(
|
||||
angle=self.angle,
|
||||
start_angle=self.start_angle,
|
||||
n_components=self.num_components,
|
||||
def round_corners(self, radius=0.5):
|
||||
vertices = self.get_vertices()
|
||||
arcs = []
|
||||
for v1, v2, v3 in adjacent_n_tuples(vertices, 3):
|
||||
vect1 = v2 - v1
|
||||
vect2 = v3 - v2
|
||||
unit_vect1 = normalize(vect1)
|
||||
unit_vect2 = normalize(vect2)
|
||||
angle = angle_between_vectors(vect1, vect2)
|
||||
# Negative radius gives concave curves
|
||||
angle *= np.sign(radius)
|
||||
# Distance between vertex and start of the arc
|
||||
cut_off_length = radius * np.tan(angle / 2)
|
||||
# Determines counterclockwise vs. clockwise
|
||||
sign = np.sign(np.cross(vect1, vect2)[2])
|
||||
arc = OpenGLArcBetweenPoints(
|
||||
v2 - unit_vect1 * cut_off_length,
|
||||
v2 + unit_vect2 * cut_off_length,
|
||||
angle=sign * angle,
|
||||
n_components=2,
|
||||
)
|
||||
)
|
||||
self.scale(self.radius, about_point=ORIGIN)
|
||||
self.shift(self.arc_center)
|
||||
arcs.append(arc)
|
||||
|
||||
@staticmethod
|
||||
def create_quadratic_bezier_points(angle, start_angle=0, n_components=8):
|
||||
samples = np.array(
|
||||
[
|
||||
[np.cos(a), np.sin(a), 0]
|
||||
for a in np.linspace(
|
||||
start_angle,
|
||||
start_angle + angle,
|
||||
2 * n_components + 1,
|
||||
)
|
||||
]
|
||||
)
|
||||
theta = angle / n_components
|
||||
samples[1::2] /= np.cos(theta / 2)
|
||||
|
||||
points = np.zeros((3 * n_components, 3))
|
||||
points[0::3] = samples[0:-1:2]
|
||||
points[1::3] = samples[1::2]
|
||||
points[2::3] = samples[2::2]
|
||||
return points
|
||||
|
||||
def get_arc_center(self):
|
||||
"""
|
||||
Looks at the normals to the first two
|
||||
anchors, and finds their intersection points
|
||||
"""
|
||||
# First two anchors and handles
|
||||
a1, h, a2 = self.get_points()[:3]
|
||||
# Tangent vectors
|
||||
t1 = h - a1
|
||||
t2 = h - a2
|
||||
# Normals
|
||||
n1 = rotate_vector(t1, TAU / 4)
|
||||
n2 = rotate_vector(t2, TAU / 4)
|
||||
return find_intersection(a1, n1, a2, n2)
|
||||
|
||||
def get_start_angle(self):
|
||||
angle = angle_of_vector(self.get_start() - self.get_arc_center())
|
||||
return angle % TAU
|
||||
|
||||
def get_stop_angle(self):
|
||||
angle = angle_of_vector(self.get_end() - self.get_arc_center())
|
||||
return angle % TAU
|
||||
|
||||
def move_arc_center_to(self, point):
|
||||
self.shift(point - self.get_arc_center())
|
||||
self.clear_points()
|
||||
# To ensure that we loop through starting with last
|
||||
arcs = [arcs[-1], *arcs[:-1]]
|
||||
for arc1, arc2 in adjacent_pairs(arcs):
|
||||
self.append_points(arc1.get_points())
|
||||
line = OpenGLLine(arc1.get_end(), arc2.get_start())
|
||||
# Make sure anchors are evenly distributed
|
||||
len_ratio = line.get_length() / arc1.get_arc_length()
|
||||
line.insert_n_curves(int(arc1.get_num_curves() * len_ratio))
|
||||
self.append_points(line.get_points())
|
||||
return self
|
||||
|
||||
|
||||
class TestOpenGLArcBetweenPoints(TestOpenGLArc):
|
||||
def __init__(self, start, end, angle=TAU / 4, **kwargs):
|
||||
super().__init__(angle=angle, **kwargs)
|
||||
if angle == 0:
|
||||
self.set_points_as_corners([LEFT, RIGHT])
|
||||
self.put_start_and_end_on(start, end)
|
||||
class OpenGLRegularPolygon(OpenGLPolygon):
|
||||
def __init__(self, n=6, start_angle=None, **kwargs):
|
||||
self.start_angle = start_angle
|
||||
if self.start_angle is None:
|
||||
if n % 2 == 0:
|
||||
self.start_angle = 0
|
||||
else:
|
||||
self.start_angle = 90 * DEGREES
|
||||
start_vect = rotate_vector(RIGHT, self.start_angle)
|
||||
vertices = compass_directions(n, start_vect)
|
||||
super().__init__(*vertices, **kwargs)
|
||||
|
||||
|
||||
class OpenGLTriangle(OpenGLRegularPolygon):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(n=3, **kwargs)
|
||||
|
||||
|
||||
class OpenGLArrowTip(OpenGLTriangle):
|
||||
def __init__(
|
||||
self,
|
||||
fill_opacity=1,
|
||||
fill_color=WHITE,
|
||||
stroke_width=0,
|
||||
width=DEFAULT_ARROW_TIP_WIDTH,
|
||||
length=DEFAULT_ARROW_TIP_LENGTH,
|
||||
angle=0,
|
||||
**kwargs
|
||||
):
|
||||
OpenGLTriangle.__init__(
|
||||
self,
|
||||
start_angle=0,
|
||||
fill_opacity=fill_opacity,
|
||||
fill_color=fill_color,
|
||||
stroke_width=stroke_width,
|
||||
**kwargs
|
||||
)
|
||||
self.set_width(width, stretch=True)
|
||||
self.set_height(length, stretch=True)
|
||||
|
||||
def get_base(self):
|
||||
return self.point_from_proportion(0.5)
|
||||
|
||||
def get_tip_point(self):
|
||||
return self.get_points()[0]
|
||||
|
||||
def get_vector(self):
|
||||
return self.get_tip_point() - self.get_base()
|
||||
|
||||
def get_angle(self):
|
||||
return angle_of_vector(self.get_vector())
|
||||
|
||||
def get_length(self):
|
||||
return get_norm(self.get_vector())
|
||||
|
||||
|
||||
class OpenGLRectangle(OpenGLPolygon):
|
||||
def __init__(
|
||||
self,
|
||||
color=WHITE,
|
||||
width=4.0,
|
||||
height=2.0,
|
||||
mark_paths_closed=True,
|
||||
close_new_points=True,
|
||||
**kwargs
|
||||
):
|
||||
self.mark_paths_closed = mark_paths_closed
|
||||
self.close_new_points = close_new_points
|
||||
OpenGLPolygon.__init__(self, UR, UL, DL, DR, color=color, **kwargs)
|
||||
|
||||
self.set_width(width, stretch=True)
|
||||
self.set_height(height, stretch=True)
|
||||
|
||||
|
||||
class OpenGLSquare(OpenGLRectangle):
|
||||
def __init__(self, side_length=2.0, **kwargs):
|
||||
self.side_length = side_length
|
||||
|
||||
super().__init__(height=side_length, width=side_length, **kwargs)
|
||||
|
||||
|
||||
class OpenGLRoundedRectangle(OpenGLRectangle):
|
||||
def __init__(self, corner_radius=0.5, **kwargs):
|
||||
self.corner_radius = corner_radius
|
||||
OpenGLRectangle.__init__(self, **kwargs)
|
||||
self.round_corners(self.corner_radius)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ from ...utils.color import *
|
|||
# from manimlib.constants import *
|
||||
# from manimlib.mobject.mobject import Mobject
|
||||
# from manimlib.mobject.mobject import Point
|
||||
# from manimlib.utils.bezier import bezier
|
||||
from ...utils.bezier import bezier
|
||||
# from manimlib.utils.bezier import get_smooth_quadratic_bezier_handle_points
|
||||
# from manimlib.utils.bezier import get_smooth_cubic_bezier_handle_points
|
||||
# from manimlib.utils.bezier import get_quadratic_approximation_of_cubic
|
||||
# from manimlib.utils.bezier import interpolate
|
||||
# from manimlib.utils.bezier import integer_interpolate
|
||||
# from manimlib.utils.bezier import partial_quadratic_bezier_points
|
||||
from ...utils.bezier import interpolate
|
||||
from ...utils.bezier import integer_interpolate
|
||||
from ...utils.bezier import partial_quadratic_bezier_points
|
||||
# from manimlib.utils.color import rgb_to_hex
|
||||
from ...utils.iterables import make_even
|
||||
|
||||
|
|
@ -26,12 +26,12 @@ from ...utils.iterables import resize_with_interpolation
|
|||
from ...utils.iterables import listify
|
||||
|
||||
# from manimlib.utils.space_ops import angle_between_vectors
|
||||
# from manimlib.utils.space_ops import cross2d
|
||||
# from manimlib.utils.space_ops import earclip_triangulation
|
||||
from ...utils.space_ops import cross2d
|
||||
from ...utils.space_ops import earclip_triangulation
|
||||
from ...utils.space_ops import get_norm
|
||||
from ...utils.space_ops import get_unit_normal
|
||||
|
||||
# from manimlib.utils.space_ops import z_to_vector
|
||||
from ...utils.space_ops import z_to_vector
|
||||
# from manimlib.shader_wrapper import ShaderWrapper
|
||||
|
||||
|
||||
|
|
@ -741,7 +741,7 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
return self
|
||||
|
||||
def pointwise_become_partial(self, vmobject, a, b):
|
||||
assert isinstance(vmobject, VMobject)
|
||||
assert isinstance(vmobject, OpenGLVMobject)
|
||||
if a <= 0 and b >= 1:
|
||||
self.become(vmobject)
|
||||
return self
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
from ..mobject.opengl_mobject import *
|
||||
from ..mobject.types.opengl_vectorized_mobject import *
|
||||
from ..mobject.opengl_geometry import *
|
||||
|
||||
Arc = TestOpenGLArc
|
||||
ArcBetweenPoints = TestOpenGLArcBetweenPoints
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ from ..mobject.types.vectorized_mobject import VMobject
|
|||
import itertools as it
|
||||
import time
|
||||
from .. import logger
|
||||
from ..constants import *
|
||||
from ..utils.space_ops import cross2d
|
||||
from ..utils.space_ops import earclip_triangulation
|
||||
from ..utils.space_ops import z_to_vector
|
||||
|
||||
from ..mobject import opengl_geometry
|
||||
|
||||
|
|
@ -231,7 +235,7 @@ class OpenGLRenderer:
|
|||
crosses = cross2d(v01s, v12s)
|
||||
convexities = np.sign(crosses)
|
||||
|
||||
atol = tolerance_for_point_equality
|
||||
atol = mob.tolerance_for_point_equality
|
||||
end_of_loop = np.zeros(len(b0s), dtype=bool)
|
||||
end_of_loop[:-1] = (np.abs(b2s[:-1] - b0s[1:]) > atol).any(1)
|
||||
end_of_loop[-1] = True
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
__all__ = [
|
||||
"bezier",
|
||||
"partial_bezier_points",
|
||||
"partial_quadratic_bezier_points",
|
||||
"interpolate",
|
||||
"integer_interpolate",
|
||||
"mid",
|
||||
|
|
@ -76,6 +77,28 @@ def partial_bezier_points(points: np.ndarray, a: float, b: float) -> np.ndarray:
|
|||
return np.array([bezier(a_to_1[: i + 1])(end_prop) for i in range(len(points))])
|
||||
|
||||
|
||||
# Shortened version of partial_bezier_points just for quadratics,
|
||||
# since this is called a fair amount
|
||||
def partial_quadratic_bezier_points(points, a, b):
|
||||
if a == 1:
|
||||
return 3 * [points[-1]]
|
||||
|
||||
def curve(t):
|
||||
return (
|
||||
points[0] * (1 - t) * (1 - t)
|
||||
+ 2 * points[1] * t * (1 - t)
|
||||
+ points[2] * t * t
|
||||
)
|
||||
|
||||
# bezier(points)
|
||||
h0 = curve(a) if a > 0 else points[0]
|
||||
h2 = curve(b) if b < 1 else points[2]
|
||||
h1_prime = (1 - a) * points[1] + a * points[2]
|
||||
end_prop = (b - a) / (1.0 - a)
|
||||
h1 = (1 - end_prop) * h0 + end_prop * h1_prime
|
||||
return [h0, h1, h2]
|
||||
|
||||
|
||||
# Linear interpolation variants
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ __all__ = [
|
|||
"find_intersection",
|
||||
"line_intersection",
|
||||
"get_winding_number",
|
||||
"cross2d",
|
||||
"earclip_triangulation",
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -34,6 +36,7 @@ from functools import reduce
|
|||
|
||||
import numpy as np
|
||||
import math
|
||||
from mapbox_earcut import triangulate_float32 as earcut
|
||||
|
||||
from ..constants import OUT
|
||||
from ..constants import PI
|
||||
|
|
@ -41,12 +44,18 @@ from ..constants import RIGHT
|
|||
from ..constants import TAU
|
||||
from ..utils.iterables import adjacent_pairs
|
||||
from ..utils.simple_functions import fdiv
|
||||
import itertools as it
|
||||
from .. import config
|
||||
|
||||
|
||||
def get_norm(vect):
|
||||
return sum([x ** 2 for x in vect]) ** 0.5
|
||||
|
||||
|
||||
def norm_squared(v):
|
||||
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]
|
||||
|
||||
|
||||
# Quaternions
|
||||
# TODO, implement quaternion type
|
||||
|
||||
|
|
@ -209,8 +218,22 @@ def cross(v1, v2):
|
|||
)
|
||||
|
||||
|
||||
def get_unit_normal(v1, v2):
|
||||
return normalize(cross(v1, v2))
|
||||
def get_unit_normal(v1, v2, tol=1e-6):
|
||||
if config["use_opengl_renderer"]:
|
||||
v1 = normalize(v1)
|
||||
v2 = normalize(v2)
|
||||
cp = cross(v1, v2)
|
||||
cp_norm = get_norm(cp)
|
||||
if cp_norm < tol:
|
||||
# Vectors align, so find a normal to them in the plane shared with the z-axis
|
||||
new_cp = cross(cross(v1, OUT), v1)
|
||||
new_cp_norm = get_norm(new_cp)
|
||||
if new_cp_norm < tol:
|
||||
return DOWN
|
||||
return new_cp / new_cp_norm
|
||||
return cp / cp_norm
|
||||
else:
|
||||
return normalize(cross(v1, v2))
|
||||
|
||||
|
||||
###
|
||||
|
|
@ -327,3 +350,90 @@ def shoelace_direction(x_y):
|
|||
"""
|
||||
area = shoelace(x_y)
|
||||
return "CW" if area > 0 else "CCW"
|
||||
|
||||
|
||||
def cross2d(a, b):
|
||||
if len(a.shape) == 2:
|
||||
return a[:, 0] * b[:, 1] - a[:, 1] * b[:, 0]
|
||||
else:
|
||||
return a[0] * b[1] - b[0] * a[1]
|
||||
|
||||
|
||||
def earclip_triangulation(verts, ring_ends):
|
||||
"""
|
||||
Returns a list of indices giving a triangulation
|
||||
of a polygon, potentially with holes
|
||||
|
||||
- verts is a numpy array of points
|
||||
|
||||
- ring_ends is a list of indices indicating where
|
||||
the ends of new paths are
|
||||
"""
|
||||
|
||||
# First, connect all the rings so that the polygon
|
||||
# with holes is instead treated as a (very convex)
|
||||
# polygon with one edge. Do this by drawing connections
|
||||
# between rings close to each other
|
||||
rings = [list(range(e0, e1)) for e0, e1 in zip([0, *ring_ends], ring_ends)]
|
||||
attached_rings = rings[:1]
|
||||
detached_rings = rings[1:]
|
||||
loop_connections = dict()
|
||||
|
||||
while detached_rings:
|
||||
i_range, j_range = [
|
||||
list(
|
||||
filter(
|
||||
# Ignore indices that are already being
|
||||
# used to draw some connection
|
||||
lambda i: i not in loop_connections,
|
||||
it.chain(*ring_group),
|
||||
)
|
||||
)
|
||||
for ring_group in (attached_rings, detached_rings)
|
||||
]
|
||||
|
||||
# Closet point on the atttached rings to an estimated midpoint
|
||||
# of the detached rings
|
||||
tmp_j_vert = midpoint(verts[j_range[0]], verts[j_range[len(j_range) // 2]])
|
||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - tmp_j_vert))
|
||||
# Closet point of the detached rings to the aforementioned
|
||||
# point of the attached rings
|
||||
j = min(j_range, key=lambda j: norm_squared(verts[i] - verts[j]))
|
||||
# Recalculate i based on new j
|
||||
i = min(i_range, key=lambda i: norm_squared(verts[i] - verts[j]))
|
||||
|
||||
# Remember to connect the polygon at these points
|
||||
loop_connections[i] = j
|
||||
loop_connections[j] = i
|
||||
|
||||
# Move the ring which j belongs to from the
|
||||
# attached list to the detached list
|
||||
new_ring = next(filter(lambda ring: ring[0] <= j < ring[-1], detached_rings))
|
||||
detached_rings.remove(new_ring)
|
||||
attached_rings.append(new_ring)
|
||||
|
||||
# Setup linked list
|
||||
after = []
|
||||
end0 = 0
|
||||
for end1 in ring_ends:
|
||||
after.extend(range(end0 + 1, end1))
|
||||
after.append(end0)
|
||||
end0 = end1
|
||||
|
||||
# Find an ordering of indices walking around the polygon
|
||||
indices = []
|
||||
i = 0
|
||||
for x in range(len(verts) + len(ring_ends) - 1):
|
||||
# starting = False
|
||||
if i in loop_connections:
|
||||
j = loop_connections[i]
|
||||
indices.extend([i, j])
|
||||
i = after[j]
|
||||
else:
|
||||
indices.append(i)
|
||||
i = after[i]
|
||||
if i == 0:
|
||||
break
|
||||
|
||||
meta_indices = earcut(verts[indices, :2], [len(indices)])
|
||||
return [indices[mi] for mi in meta_indices]
|
||||
|
|
|
|||
55
poetry.lock
generated
55
poetry.lock
generated
|
|
@ -628,6 +628,17 @@ category = "main"
|
|||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "mapbox-earcut"
|
||||
version = "0.12.10"
|
||||
description = "Python bindings for the mapbox earcut C++ polygon triangulation library."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
numpy = "*"
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "1.1.1"
|
||||
|
|
@ -1493,7 +1504,7 @@ webgl_renderer = ["grpcio", "grpcio-tools", "watchdog"]
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.2"
|
||||
content-hash = "0396636548f0b80f59e84ff57b7b9e33dfc4330726c4a5e818760ead768bf58d"
|
||||
content-hash = "965cf2774981ec78ba20c7e33026afd4afdce0d6face6145b034f6efa0317cfe"
|
||||
|
||||
[metadata.files]
|
||||
alabaster = [
|
||||
|
|
@ -1943,6 +1954,48 @@ manimpango = [
|
|||
{file = "ManimPango-0.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:515649c1dd574121efa2cb3654a3fef8491bd2c4fafdd4176edda41d0720ea49"},
|
||||
{file = "ManimPango-0.2.3.tar.gz", hash = "sha256:6fe4fe0a8623b52de96393e9b2275cce7734ff92e674391e6a10baac84abf4de"},
|
||||
]
|
||||
mapbox-earcut = [
|
||||
{file = "mapbox_earcut-0.12.10-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:2aaf7bdb002d36d38d1412088329a9176de3fcadc24a53951b76e49a27e34d78"},
|
||||
{file = "mapbox_earcut-0.12.10-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddda755c63cf5f9bff338cd375e4e937035898827fef960102e2f4c8984cb068"},
|
||||
{file = "mapbox_earcut-0.12.10-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3475382dd573986d0dfe3f6b6fd3b1f576e74af7fd7d95066d2506ce8f4b38e5"},
|
||||
{file = "mapbox_earcut-0.12.10-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f2b9079df53258766c3108a1c46a7942963c26f216f9bc2d216f93eeed1da76c"},
|
||||
{file = "mapbox_earcut-0.12.10-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:124cc27486d0e9c9b185a6d80e551e736e2bc81d28e077e297ac39980c14e588"},
|
||||
{file = "mapbox_earcut-0.12.10-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:04228ff704d5edc49d71ba3fd07c4a8621a4f416d2ad5c2504a80d0cabd51c18"},
|
||||
{file = "mapbox_earcut-0.12.10-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:9a15cb52f3f6d5980e41b6ca31976cc2048acf290f2e5159d7527e16e5c5d748"},
|
||||
{file = "mapbox_earcut-0.12.10-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:6b083a065afd40961cc311b425c54275f531d91a27bdd34a2049993a68d8a651"},
|
||||
{file = "mapbox_earcut-0.12.10-cp35-cp35m-win32.whl", hash = "sha256:fa3355248d3beaab67438373315e55a37ecc2694a4a4b1965e9d8f582a210479"},
|
||||
{file = "mapbox_earcut-0.12.10-cp35-cp35m-win_amd64.whl", hash = "sha256:90b2977a9eda2a1873e44d7821ecf3b18062aa0878e957cb306e6018aba105c9"},
|
||||
{file = "mapbox_earcut-0.12.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:553688de45e73fa7d192fd6c29b87a4c5ea60a728b3c7b4c5f1715a89e6a8e54"},
|
||||
{file = "mapbox_earcut-0.12.10-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:498b7f35c6a7c5c25829bcf1a79015ae64e94e8e36b84bd06e93131352b409f0"},
|
||||
{file = "mapbox_earcut-0.12.10-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:8b9c6147249edf5049ccf7a7bca5bb29c9d512b3a5b62510659cfc51f1b8f806"},
|
||||
{file = "mapbox_earcut-0.12.10-cp36-cp36m-win32.whl", hash = "sha256:4eaf2a9e86bbf808bf045eb587ec93a393e9d75ab87e7c1c9c8a5d9e122e30ac"},
|
||||
{file = "mapbox_earcut-0.12.10-cp36-cp36m-win_amd64.whl", hash = "sha256:cb82e4ea9a67a9cdd46c9fc39bcb24e11f49569010368f9c5d376489bb2d0317"},
|
||||
{file = "mapbox_earcut-0.12.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:619a31a4d9748a30fbea82fd45beec6ab9e7f8a7ae7818be46f09d03ea0d9185"},
|
||||
{file = "mapbox_earcut-0.12.10-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:054cd56cf8adf179d0aa0e8a061774e5afa767aaf6507851f4d2731b58f03d43"},
|
||||
{file = "mapbox_earcut-0.12.10-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ebeef32d504bf7e1ba986f86d87a8d3c96a54b6e03b0535ccdf0ce8f2f56e563"},
|
||||
{file = "mapbox_earcut-0.12.10-cp37-cp37m-win32.whl", hash = "sha256:62a8787e30f7bbc6e1ebe8b11591119ed85aa5044b2cf3f6afb058e6fffeb8a3"},
|
||||
{file = "mapbox_earcut-0.12.10-cp37-cp37m-win_amd64.whl", hash = "sha256:5f854a887f854bdea62c4b58785b82cf70056d53dc3bf23146ce68c125ceed96"},
|
||||
{file = "mapbox_earcut-0.12.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f16be13a40d20419362f8ce27cf2dca8f0b8f4c7caa24caaff8351bcdf853e"},
|
||||
{file = "mapbox_earcut-0.12.10-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:cac7c8c332d1e802f478b5b79166ab38005b6d1bd908fb96857a8941e702f70d"},
|
||||
{file = "mapbox_earcut-0.12.10-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:24cc595987932b5d47b93ddb733c6bd084d91ff0af66ff2205aa717f0a5a13b8"},
|
||||
{file = "mapbox_earcut-0.12.10-cp38-cp38-win32.whl", hash = "sha256:fe4b4e1c53fe0fde8e5fee86d868344f47d4d9fd89d9599548ea4b98349f10d6"},
|
||||
{file = "mapbox_earcut-0.12.10-cp38-cp38-win_amd64.whl", hash = "sha256:6f4601b949ab43cf21dca86ffb6b4e2b30974ab3112f089d16b5f365e8b446f4"},
|
||||
{file = "mapbox_earcut-0.12.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ca00bdcb162b6a543f3061f49e2dcde42d6b780a545b5054da437e4ff5e33905"},
|
||||
{file = "mapbox_earcut-0.12.10-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:031132cdfa4d20e640f24d6817b65a91e3292c34ed527b4309617c9992284133"},
|
||||
{file = "mapbox_earcut-0.12.10-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f284c0fc7f3c84664e9dddabae4eae729e6458e96ec5f30c9d7000c9b0dadca7"},
|
||||
{file = "mapbox_earcut-0.12.10-cp39-cp39-win32.whl", hash = "sha256:c94a263767a841a5f7e8f03c725bdf41c87b8936c1a5fe18a5a77f3b9344d68f"},
|
||||
{file = "mapbox_earcut-0.12.10-cp39-cp39-win_amd64.whl", hash = "sha256:742bd1f94113f44f1adcb5f3e2dca6e288695645b7eeab5d94f589dacda092af"},
|
||||
{file = "mapbox_earcut-0.12.10-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:ad76defd6188e71023e43e5aed32ab5d540c0452a74918fc5de9bcccdee6c053"},
|
||||
{file = "mapbox_earcut-0.12.10-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:a3a9a81f696627f3c331f16c8d628b494949fa68067a42d2f1a6a56ee115eb0d"},
|
||||
{file = "mapbox_earcut-0.12.10-pp27-pypy_73-win32.whl", hash = "sha256:044f70ed230bdb94f08356ec84f4bd30e4ea493d63700c3cc6fa86c5cf57623e"},
|
||||
{file = "mapbox_earcut-0.12.10-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e352482e6022cc113dd008886012f0966bd2511933f888805fa87f070423d5b1"},
|
||||
{file = "mapbox_earcut-0.12.10-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:41a282c485e96135ff15c01e0e1eb334cf1a05235edde8154b7b6e30187e54f4"},
|
||||
{file = "mapbox_earcut-0.12.10-pp36-pypy36_pp73-win32.whl", hash = "sha256:5e1e3cf5a881eafeba7a7e5c4b1b9e7376710c48513513894e2a082ebf870326"},
|
||||
{file = "mapbox_earcut-0.12.10-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7b6c2bfc28dc82a27071079885ffbe0836c0ad5dbd4ab47a5edaa24a86218daf"},
|
||||
{file = "mapbox_earcut-0.12.10-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6e6614f7b437b4b0a69ab642cf35569079408ac25c193013d5ad26e82c71fc01"},
|
||||
{file = "mapbox_earcut-0.12.10-pp37-pypy37_pp73-win32.whl", hash = "sha256:d016823e863309b9cc542bb1d3bcdc274ec65fb23f2566de298188dcd4a75c0e"},
|
||||
{file = "mapbox_earcut-0.12.10.tar.gz", hash = "sha256:50d995673ac9ce8cb9abb7ab64b709d611c1d27557907e00b631b5272345c453"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
|
||||
{file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ watchdog = { version = "*", optional = true }
|
|||
jupyterlab = { version = "^3.0", optional = true }
|
||||
moderngl = "^5.6.3"
|
||||
moderngl-window = "^2.3.0"
|
||||
mapbox-earcut = "^0.12.10"
|
||||
|
||||
[tool.poetry.extras]
|
||||
webgl_renderer = ["grpcio","grpcio-tools","watchdog"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue