mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
Changing the way VMobject handles its internal bezier curves so as to eliminate the need for sup_paths
This commit is contained in:
parent
eb0c63606d
commit
eaf25ff34b
29 changed files with 432 additions and 278 deletions
|
@ -206,11 +206,7 @@ class Camera(object):
|
||||||
else:
|
else:
|
||||||
method = Mobject.get_family
|
method = Mobject.get_family
|
||||||
return remove_list_redundancies(list(
|
return remove_list_redundancies(list(
|
||||||
it.chain(*[
|
it.chain(*[method(m) for m in mobjects])
|
||||||
method(m)
|
|
||||||
for m in mobjects
|
|
||||||
if not (isinstance(m, VMobject) and m.is_subpath)
|
|
||||||
])
|
|
||||||
))
|
))
|
||||||
|
|
||||||
def get_mobjects_to_display(
|
def get_mobjects_to_display(
|
||||||
|
@ -324,32 +320,61 @@ class Camera(object):
|
||||||
self.display_vectorized(vmobject, ctx)
|
self.display_vectorized(vmobject, ctx)
|
||||||
|
|
||||||
def display_vectorized(self, vmobject, ctx):
|
def display_vectorized(self, vmobject, ctx):
|
||||||
if vmobject.is_subpath:
|
|
||||||
# Subpath vectorized mobjects are taken care
|
|
||||||
# of by their parent
|
|
||||||
return
|
|
||||||
self.set_cairo_context_path(ctx, vmobject)
|
self.set_cairo_context_path(ctx, vmobject)
|
||||||
self.apply_stroke(ctx, vmobject, background=True)
|
self.apply_stroke(ctx, vmobject, background=True)
|
||||||
self.apply_fill(ctx, vmobject)
|
self.apply_fill(ctx, vmobject)
|
||||||
self.apply_stroke(ctx, vmobject)
|
self.apply_stroke(ctx, vmobject)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# def old_set_cairo_context_path(self, ctx, vmobject):
|
||||||
|
# ctx.new_path()
|
||||||
|
# for vmob in it.chain([vmobject], vmobject.get_subpath_mobjects()):
|
||||||
|
# points = self.transform_points_pre_display(
|
||||||
|
# vmob, vmob.points
|
||||||
|
# )
|
||||||
|
# if np.any(np.isnan(points)) or np.any(points == np.inf):
|
||||||
|
# # TODO, print some kind of warning about
|
||||||
|
# # mobject having invalid points?
|
||||||
|
# points = np.zeros((1, 3))
|
||||||
|
# ctx.new_sub_path()
|
||||||
|
# ctx.move_to(*points[0][:2])
|
||||||
|
# for p0, p1, p2 in zip(points[1::3], points[2::3], points[3::3]):
|
||||||
|
# ctx.curve_to(*p0[:2], *p1[:2], *p2[:2])
|
||||||
|
# if vmob.is_closed():
|
||||||
|
# ctx.close_path()
|
||||||
|
# return self
|
||||||
|
|
||||||
def set_cairo_context_path(self, ctx, vmobject):
|
def set_cairo_context_path(self, ctx, vmobject):
|
||||||
|
# self.old_set_cairo_context_path(ctx, vmobject)
|
||||||
|
# return
|
||||||
|
|
||||||
|
points = vmobject.get_points()
|
||||||
|
if len(points) == 0:
|
||||||
|
return
|
||||||
|
elif np.any(np.isnan(points)) or np.any(points == np.inf):
|
||||||
|
# TODO, print some kind of warning about
|
||||||
|
# mobject having invalid points?
|
||||||
|
points = np.zeros((1, 3))
|
||||||
|
|
||||||
|
def should_start_new_path(last_p3, p0):
|
||||||
|
if last_p3 is None:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return not vmobject.consider_points_equals(
|
||||||
|
last_p3, p0
|
||||||
|
)
|
||||||
|
|
||||||
|
last_p3 = None
|
||||||
|
quads = vmobject.get_all_cubic_bezier_point_tuples()
|
||||||
ctx.new_path()
|
ctx.new_path()
|
||||||
for vmob in it.chain([vmobject], vmobject.get_subpath_mobjects()):
|
for p0, p1, p2, p3 in quads:
|
||||||
points = self.transform_points_pre_display(
|
if should_start_new_path(last_p3, p0):
|
||||||
vmob, vmob.points
|
ctx.new_sub_path()
|
||||||
)
|
ctx.move_to(*p0[:2])
|
||||||
if np.any(np.isnan(points)) or np.any(points == np.inf):
|
ctx.curve_to(*p1[:2], *p2[:2], *p3[:2])
|
||||||
points = np.zeros((1, 3))
|
last_p3 = p3
|
||||||
ctx.new_sub_path()
|
if vmobject.is_closed():
|
||||||
ctx.move_to(*points[0][:2])
|
ctx.close_path()
|
||||||
for triplet in zip(points[1::3], points[2::3], points[3::3]):
|
|
||||||
ctx.curve_to(*it.chain(*[
|
|
||||||
point[:2] for point in triplet
|
|
||||||
]))
|
|
||||||
if vmob.is_closed():
|
|
||||||
ctx.close_path()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_cairo_context_color(self, ctx, rgbas, vmobject):
|
def set_cairo_context_color(self, ctx, rgbas, vmobject):
|
||||||
|
|
|
@ -12,7 +12,7 @@ from manimlib.utils.config_ops import digest_config
|
||||||
class MappingCamera(Camera):
|
class MappingCamera(Camera):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"mapping_func": lambda p: p,
|
"mapping_func": lambda p: p,
|
||||||
"min_anchor_points": 50,
|
"min_num_curves": 50,
|
||||||
"allow_object_intrusion": False
|
"allow_object_intrusion": False
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ class MappingCamera(Camera):
|
||||||
mobject_copies = [mobject.copy() for mobject in mobjects]
|
mobject_copies = [mobject.copy() for mobject in mobjects]
|
||||||
for mobject in mobject_copies:
|
for mobject in mobject_copies:
|
||||||
if isinstance(mobject, VMobject) and \
|
if isinstance(mobject, VMobject) and \
|
||||||
0 < mobject.get_num_anchor_points() < self.min_anchor_points:
|
0 < mobject.get_num_curves() < self.min_num_curves:
|
||||||
mobject.insert_n_anchor_points(self.min_anchor_points)
|
mobject.insert_n_curves(self.min_num_curves)
|
||||||
Camera.capture_mobjects(
|
Camera.capture_mobjects(
|
||||||
self, mobject_copies,
|
self, mobject_copies,
|
||||||
include_submobjects=False,
|
include_submobjects=False,
|
||||||
|
|
|
@ -324,12 +324,12 @@ class NumberPlane(VMobject):
|
||||||
arrow = Arrow(ORIGIN, point, **kwargs)
|
arrow = Arrow(ORIGIN, point, **kwargs)
|
||||||
return arrow
|
return arrow
|
||||||
|
|
||||||
def prepare_for_nonlinear_transform(self, num_inserted_anchor_points=50):
|
def prepare_for_nonlinear_transform(self, num_inserted_curves=50):
|
||||||
for mob in self.family_members_with_points():
|
for mob in self.family_members_with_points():
|
||||||
num_anchors = mob.get_num_anchor_points()
|
num_curves = mob.get_num_curves()
|
||||||
if num_inserted_anchor_points > num_anchors:
|
if num_inserted_curves > num_curves:
|
||||||
mob.insert_n_anchor_points(
|
mob.insert_n_curves(
|
||||||
num_inserted_anchor_points - num_anchors)
|
num_inserted_curves - num_curves)
|
||||||
mob.make_smooth()
|
mob.make_smooth()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -43,12 +43,16 @@ class Arc(VMobject):
|
||||||
# Appropriate tangent lines to the circle
|
# Appropriate tangent lines to the circle
|
||||||
d_theta = self.angle / (self.num_anchors - 1.0)
|
d_theta = self.angle / (self.num_anchors - 1.0)
|
||||||
tangent_vectors = np.zeros(anchors.shape)
|
tangent_vectors = np.zeros(anchors.shape)
|
||||||
|
# Rotate all 90 degress, via (x, y) -> (-y, x)
|
||||||
tangent_vectors[:, 1] = anchors[:, 0]
|
tangent_vectors[:, 1] = anchors[:, 0]
|
||||||
tangent_vectors[:, 0] = -anchors[:, 1]
|
tangent_vectors[:, 0] = -anchors[:, 1]
|
||||||
|
# Use tangent vectors to deduce anchors
|
||||||
handles1 = anchors[:-1] + (d_theta / 3) * tangent_vectors[:-1]
|
handles1 = anchors[:-1] + (d_theta / 3) * tangent_vectors[:-1]
|
||||||
handles2 = anchors[1:] - (d_theta / 3) * tangent_vectors[1:]
|
handles2 = anchors[1:] - (d_theta / 3) * tangent_vectors[1:]
|
||||||
self.set_anchors_and_handles(
|
self.set_anchors_and_handles(
|
||||||
anchors, handles1, handles2
|
anchors[:-1],
|
||||||
|
handles1, handles2,
|
||||||
|
anchors[1:],
|
||||||
)
|
)
|
||||||
self.scale(self.radius, about_point=ORIGIN)
|
self.scale(self.radius, about_point=ORIGIN)
|
||||||
self.shift(self.arc_center)
|
self.shift(self.arc_center)
|
||||||
|
@ -250,9 +254,9 @@ class AnnularSector(VMobject):
|
||||||
for alpha in np.linspace(0, 1, 4)
|
for alpha in np.linspace(0, 1, 4)
|
||||||
])
|
])
|
||||||
self.points = np.array(arc1.points)
|
self.points = np.array(arc1.points)
|
||||||
self.add_control_points(a1_to_a2_points[1:])
|
self.add_cubic_bezier_curve(*a1_to_a2_points[1:])
|
||||||
self.add_control_points(arc2.points[1:])
|
self.add_cubic_bezier_curve(*arc2.points[1:])
|
||||||
self.add_control_points(a2_to_a1_points[1:])
|
self.add_cubic_bezier_curve(*a2_to_a1_points[1:])
|
||||||
|
|
||||||
def get_arc_center(self):
|
def get_arc_center(self):
|
||||||
first_point = self.points[0]
|
first_point = self.points[0]
|
||||||
|
|
|
@ -331,7 +331,7 @@ class VMobjectFromSVGPathstring(VMobject):
|
||||||
re.split(pattern, self.path_string)[1:]
|
re.split(pattern, self.path_string)[1:]
|
||||||
))
|
))
|
||||||
# Which mobject should new points be added to
|
# Which mobject should new points be added to
|
||||||
self.growing_path = self
|
self = self
|
||||||
for command, coord_string in pairs:
|
for command, coord_string in pairs:
|
||||||
self.handle_command(command, coord_string)
|
self.handle_command(command, coord_string)
|
||||||
# people treat y-coordinate differently
|
# people treat y-coordinate differently
|
||||||
|
@ -342,28 +342,23 @@ class VMobjectFromSVGPathstring(VMobject):
|
||||||
command = command.upper()
|
command = command.upper()
|
||||||
# new_points are the points that will be added to the curr_points
|
# new_points are the points that will be added to the curr_points
|
||||||
# list. This variable may get modified in the conditionals below.
|
# list. This variable may get modified in the conditionals below.
|
||||||
points = self.growing_path.points
|
points = self.points
|
||||||
new_points = self.string_to_points(coord_string)
|
new_points = self.string_to_points(coord_string)
|
||||||
|
|
||||||
if command == "M": # moveto
|
|
||||||
if isLower and len(points) > 0:
|
|
||||||
new_points[0] += points[-1]
|
|
||||||
if len(points) > 0:
|
|
||||||
self.growing_path = self.add_subpath(new_points[:1])
|
|
||||||
else:
|
|
||||||
self.growing_path.start_at(new_points[0])
|
|
||||||
|
|
||||||
if len(new_points) <= 1:
|
|
||||||
return
|
|
||||||
|
|
||||||
points = self.growing_path.points
|
|
||||||
new_points = new_points[1:]
|
|
||||||
command = "L"
|
|
||||||
|
|
||||||
if isLower and len(points) > 0:
|
if isLower and len(points) > 0:
|
||||||
new_points += points[-1]
|
new_points += points[-1]
|
||||||
|
|
||||||
if command in ["L", "H", "V"]: # lineto
|
if command == "M": # moveto
|
||||||
|
self.start_new_path(new_points[0])
|
||||||
|
|
||||||
|
if len(new_points) <= 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Huh? When does this come up?
|
||||||
|
points = self.points
|
||||||
|
new_points = new_points[1:]
|
||||||
|
command = "L"
|
||||||
|
elif command in ["L", "H", "V"]: # lineto
|
||||||
if command == "H":
|
if command == "H":
|
||||||
new_points[0, 1] = points[-1, 1]
|
new_points[0, 1] = points[-1, 1]
|
||||||
elif command == "V":
|
elif command == "V":
|
||||||
|
@ -372,21 +367,27 @@ class VMobjectFromSVGPathstring(VMobject):
|
||||||
new_points[0, 0] += points[-1, 1]
|
new_points[0, 0] += points[-1, 1]
|
||||||
new_points[0, 1] = new_points[0, 0]
|
new_points[0, 1] = new_points[0, 0]
|
||||||
new_points[0, 0] = points[-1, 0]
|
new_points[0, 0] = points[-1, 0]
|
||||||
new_points = new_points.repeat(3, axis=0)
|
self.add_line_to(new_points[0])
|
||||||
elif command == "C": # curveto
|
return
|
||||||
|
|
||||||
|
if command == "C": # curveto
|
||||||
pass # Yay! No action required
|
pass # Yay! No action required
|
||||||
elif command in ["S", "T"]: # smooth curveto
|
elif command in ["S", "T"]: # smooth curveto
|
||||||
handle1 = points[-1] + (points[-1] - points[-2])
|
self.add_smooth_curve_to(*new_points)
|
||||||
new_points = np.append([handle1], new_points, axis=0)
|
# handle1 = points[-1] + (points[-1] - points[-2])
|
||||||
if command in ["Q", "T"]: # quadratic Bezier curve
|
# new_points = np.append([handle1], new_points, axis=0)
|
||||||
|
return
|
||||||
|
elif command == "Q": # quadratic Bezier curve
|
||||||
# TODO, this is a suboptimal approximation
|
# TODO, this is a suboptimal approximation
|
||||||
new_points = np.append([new_points[0]], new_points, axis=0)
|
new_points = np.append([new_points[0]], new_points, axis=0)
|
||||||
elif command == "A": # elliptical Arc
|
elif command == "A": # elliptical Arc
|
||||||
raise Exception("Not implemented")
|
raise Exception("Not implemented")
|
||||||
elif command == "Z": # closepath
|
elif command == "Z": # closepath
|
||||||
if not is_closed(points):
|
if is_closed(points):
|
||||||
# Both handles and new anchor are the start
|
return
|
||||||
new_points = points[[0, 0, 0]]
|
# Both handles and new anchor are the start
|
||||||
|
# TODO, is this needed?
|
||||||
|
new_points = points[[0, 0, 0]]
|
||||||
# self.mark_paths_closed = True
|
# self.mark_paths_closed = True
|
||||||
|
|
||||||
# Handle situations where there's multiple relative control points
|
# Handle situations where there's multiple relative control points
|
||||||
|
@ -395,7 +396,7 @@ class VMobjectFromSVGPathstring(VMobject):
|
||||||
new_points[i:i + 3] -= points[-1]
|
new_points[i:i + 3] -= points[-1]
|
||||||
new_points[i:i + 3] += new_points[i - 1]
|
new_points[i:i + 3] += new_points[i - 1]
|
||||||
|
|
||||||
self.growing_path.add_control_points(new_points)
|
self.add_cubic_bezier_curve_to(*new_points)
|
||||||
|
|
||||||
def string_to_points(self, coord_string):
|
def string_to_points(self, coord_string):
|
||||||
numbers = string_to_numbers(coord_string)
|
numbers = string_to_numbers(coord_string)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
import sys
|
||||||
|
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ from manimlib.mobject.three_d_utils import get_3d_vmob_gradient_start_and_end_po
|
||||||
from manimlib.utils.bezier import bezier
|
from manimlib.utils.bezier import bezier
|
||||||
from manimlib.utils.bezier import get_smooth_handle_points
|
from manimlib.utils.bezier import get_smooth_handle_points
|
||||||
from manimlib.utils.bezier import interpolate
|
from manimlib.utils.bezier import interpolate
|
||||||
from manimlib.utils.bezier import is_closed
|
from manimlib.utils.bezier import integer_interpolate
|
||||||
from manimlib.utils.bezier import partial_bezier_points
|
from manimlib.utils.bezier import partial_bezier_points
|
||||||
from manimlib.utils.color import color_to_rgba
|
from manimlib.utils.color import color_to_rgba
|
||||||
from manimlib.utils.iterables import make_even
|
from manimlib.utils.iterables import make_even
|
||||||
|
@ -17,6 +18,15 @@ from manimlib.utils.iterables import tuplify
|
||||||
from manimlib.utils.simple_functions import clip_in_place
|
from manimlib.utils.simple_functions import clip_in_place
|
||||||
|
|
||||||
|
|
||||||
|
# TODO
|
||||||
|
# - Change cubic curve groups to have 4 points instead of 3
|
||||||
|
# - Change sub_path idea accordingly
|
||||||
|
# - No more mark_paths_closed, instead have the camera test
|
||||||
|
# if last point in close to first point
|
||||||
|
# - Think about length of self.points. Always 0 or 1 mod 4?
|
||||||
|
# That's kind of weird.
|
||||||
|
|
||||||
|
|
||||||
class VMobject(Mobject):
|
class VMobject(Mobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"fill_color": None,
|
"fill_color": None,
|
||||||
|
@ -38,14 +48,16 @@ class VMobject(Mobject):
|
||||||
"sheen_direction": UL,
|
"sheen_direction": UL,
|
||||||
# Indicates that it will not be displayed, but
|
# Indicates that it will not be displayed, but
|
||||||
# that it should count in parent mobject's path
|
# that it should count in parent mobject's path
|
||||||
"is_subpath": False,
|
|
||||||
"close_new_points": False,
|
"close_new_points": False,
|
||||||
"mark_paths_closed": False,
|
|
||||||
"propagate_style_to_family": False,
|
"propagate_style_to_family": False,
|
||||||
"pre_function_handle_to_anchor_scale_factor": 0.01,
|
"pre_function_handle_to_anchor_scale_factor": 0.01,
|
||||||
"make_smooth_after_applying_functions": False,
|
"make_smooth_after_applying_functions": False,
|
||||||
"background_image_file": None,
|
"background_image_file": None,
|
||||||
"shade_in_3d": False,
|
"shade_in_3d": False,
|
||||||
|
# This is within a pixel
|
||||||
|
# TODO, what if you're rather zoomed in?
|
||||||
|
"tolerance_for_point_equality": 1e-6,
|
||||||
|
"n_points_per_cubic_curve": 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_group_class(self):
|
def get_group_class(self):
|
||||||
|
@ -364,63 +376,136 @@ class VMobject(Mobject):
|
||||||
submob.z_index_group = self
|
submob.z_index_group = self
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# Drawing
|
# Points
|
||||||
def start_at(self, point):
|
def set_points(self, points):
|
||||||
if len(self.points) == 0:
|
self.points = np.array(points)
|
||||||
self.points = np.zeros((1, 3))
|
|
||||||
self.points[0] = point
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_control_points(self, control_points):
|
def get_points(self):
|
||||||
assert(len(control_points) % 3 == 0)
|
return np.array(self.points)
|
||||||
self.points = np.append(
|
|
||||||
self.points,
|
|
||||||
control_points,
|
|
||||||
axis=0
|
|
||||||
)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def is_closed(self):
|
def set_anchors_and_handles(self, anchors1, handles1, handles2, anchors2):
|
||||||
return is_closed(self.points)
|
assert(len(anchors1) == len(handles1) == len(handles2) == len(anchors2))
|
||||||
|
nppcc = self.n_points_per_cubic_curve # 4
|
||||||
def set_anchors_and_handles(self, anchors, handles1, handles2):
|
total_len = nppcc * len(anchors1)
|
||||||
assert(len(anchors) == len(handles1) + 1)
|
|
||||||
assert(len(anchors) == len(handles2) + 1)
|
|
||||||
total_len = 3 * (len(anchors) - 1) + 1
|
|
||||||
self.points = np.zeros((total_len, self.dim))
|
self.points = np.zeros((total_len, self.dim))
|
||||||
self.points[0] = anchors[0]
|
arrays = [anchors1, handles1, handles2, anchors2]
|
||||||
arrays = [handles1, handles2, anchors[1:]]
|
|
||||||
for index, array in enumerate(arrays):
|
for index, array in enumerate(arrays):
|
||||||
self.points[index + 1::3] = array
|
self.points[index::nppcc] = array
|
||||||
return self.points
|
return self
|
||||||
|
|
||||||
def set_points_as_corners(self, points):
|
def clear_points(self):
|
||||||
if len(points) <= 1:
|
self.points = np.zeros((0, self.dim))
|
||||||
return self
|
|
||||||
points = self.prepare_new_anchor_points(points)
|
def append_points(self, new_points):
|
||||||
self.set_anchors_and_handles(points, *[
|
# TODO, check that number new points is a multiple of 4?
|
||||||
interpolate(points[:-1], points[1:], alpha)
|
# or else that if len(self.points) % 4 == 1, then
|
||||||
for alpha in (1. / 3, 2. / 3)
|
# len(new_points) % 4 == 3?
|
||||||
|
self.points = np.append(self.points, new_points, axis=0)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def start_new_path(self, point):
|
||||||
|
# TODO, make sure that len(self.points) % 4 == 0?
|
||||||
|
self.append_points([point])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add_cubic_bezier_curve(self, anchor1, handle1, handle2, anchor2):
|
||||||
|
# TODO, check the len(self.points) % 4 == 0?
|
||||||
|
self.append_points([anchor1, handle1, handle2, anchor2])
|
||||||
|
|
||||||
|
def add_cubic_bezier_curve_to(self, handle1, handle2, anchor):
|
||||||
|
"""
|
||||||
|
Add cubic bezier curve to the path.
|
||||||
|
"""
|
||||||
|
self.throw_error_if_no_points()
|
||||||
|
new_points = [handle1, handle2, anchor]
|
||||||
|
if self.has_new_path_started():
|
||||||
|
self.append_points(new_points)
|
||||||
|
else:
|
||||||
|
self.append_points([self.get_last_point()] + new_points)
|
||||||
|
|
||||||
|
def add_line_to(self, point):
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
|
self.add_cubic_bezier_curve_to(*[
|
||||||
|
interpolate(self.get_last_point(), point, a)
|
||||||
|
for a in np.linspace(0, 1, nppcc)[1:]
|
||||||
])
|
])
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_points_smoothly(self, points):
|
def add_smooth_curve_to(self, *points):
|
||||||
if len(points) <= 1:
|
"""
|
||||||
return self
|
If two points are passed in, the first is intepretted
|
||||||
points = self.prepare_new_anchor_points(points)
|
as a handle, the second as an anchor
|
||||||
h1, h2 = get_smooth_handle_points(points)
|
"""
|
||||||
self.set_anchors_and_handles(points, h1, h2)
|
if len(points) == 1:
|
||||||
|
handle2 = None
|
||||||
|
new_anchor = points[0]
|
||||||
|
elif len(points) == 2:
|
||||||
|
handle2, new_anchor = points
|
||||||
|
else:
|
||||||
|
name = sys._getframe(0).f_code.co_name
|
||||||
|
raise Exception("Only call {} with 1 or 2 points".format(name))
|
||||||
|
|
||||||
|
if self.has_new_path_started():
|
||||||
|
self.add_line_to(new_anchor)
|
||||||
|
else:
|
||||||
|
self.throw_error_if_no_points()
|
||||||
|
last_h2, last_a2 = self.points[-2:]
|
||||||
|
last_tangent = (last_a2 - last_h2)
|
||||||
|
handle1 = last_a2 + last_tangent
|
||||||
|
if handle2 is None:
|
||||||
|
to_anchor_vect = new_anchor - last_a2
|
||||||
|
new_tangent = rotate_vector(
|
||||||
|
last_tangent, PI, axis=to_anchor_vect
|
||||||
|
)
|
||||||
|
handle2 = new_anchor - new_tangent
|
||||||
|
self.append_points([
|
||||||
|
last_a2, handle1, handle2, new_anchor
|
||||||
|
])
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def prepare_new_anchor_points(self, points):
|
# TODO, remove
|
||||||
if not isinstance(points, np.ndarray):
|
# def add_control_points(self, control_points):
|
||||||
points = np.array(points)
|
# assert(len(control_points) % 3 == 0)
|
||||||
if self.close_new_points and not is_closed(points):
|
# self.points = np.append(
|
||||||
points = np.append(points, [points[0]], axis=0)
|
# self.points,
|
||||||
|
# control_points,
|
||||||
|
# axis=0
|
||||||
|
# )
|
||||||
|
# return self
|
||||||
|
|
||||||
|
def has_new_path_started(self):
|
||||||
|
nppcc = self.n_points_per_cubic_curve # 4
|
||||||
|
return len(self.points) % nppcc == 1
|
||||||
|
|
||||||
|
def get_last_point(self):
|
||||||
|
return self.points[-1]
|
||||||
|
|
||||||
|
def is_closed(self):
|
||||||
|
return self.consider_points_equals(
|
||||||
|
self.points[0], self.points[-1]
|
||||||
|
)
|
||||||
|
|
||||||
|
def has_no_points(self):
|
||||||
|
return len(self.points) == 0
|
||||||
|
|
||||||
|
def add_points_as_corners(self, points):
|
||||||
|
for point in points:
|
||||||
|
self.add_line_to(point)
|
||||||
return points
|
return points
|
||||||
|
|
||||||
def set_points(self, points):
|
def set_points_as_corners(self, points):
|
||||||
self.points = np.array(points)
|
self.clear_points()
|
||||||
|
self.start_new_path(points[0])
|
||||||
|
self.add_points_as_corners(points[1:])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def set_points_smoothly(self, points):
|
||||||
|
assert(len(points) > 1)
|
||||||
|
h1, h2 = get_smooth_handle_points(points)
|
||||||
|
self.set_anchors_and_handles(
|
||||||
|
points[:-1], h1, h2, points[1:]
|
||||||
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def set_anchor_points(self, points, mode="smooth"):
|
def set_anchor_points(self, points, mode="smooth"):
|
||||||
|
@ -432,6 +517,7 @@ class VMobject(Mobject):
|
||||||
raise Exception("Unknown mode")
|
raise Exception("Unknown mode")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# TODO, this will not work!
|
||||||
def change_anchor_mode(self, mode):
|
def change_anchor_mode(self, mode):
|
||||||
for submob in self.family_members_with_points():
|
for submob in self.family_members_with_points():
|
||||||
anchors = submob.get_anchors()
|
anchors = submob.get_anchors()
|
||||||
|
@ -445,35 +531,18 @@ class VMobject(Mobject):
|
||||||
return self.change_anchor_mode("corners")
|
return self.change_anchor_mode("corners")
|
||||||
|
|
||||||
def add_subpath(self, points):
|
def add_subpath(self, points):
|
||||||
"""
|
assert(len(points) % 4 == 0)
|
||||||
A VMobject is meant to represent
|
self.points = np.append(self.points, points, axis=0)
|
||||||
a single "path", in the svg sense of the word.
|
return self
|
||||||
However, one such path may really consist of separate
|
|
||||||
continuous components if there is a move_to command.
|
|
||||||
These other portions of the path will be treated as submobjects,
|
|
||||||
but will be tracked in a separate special list for when
|
|
||||||
it comes time to display.
|
|
||||||
"""
|
|
||||||
subpath_mobject = self.copy() # Really helps to be of the same class
|
|
||||||
subpath_mobject.submobjects = []
|
|
||||||
subpath_mobject.is_subpath = True
|
|
||||||
subpath_mobject.set_points(points)
|
|
||||||
self.add(subpath_mobject)
|
|
||||||
return subpath_mobject
|
|
||||||
|
|
||||||
def append_vectorized_mobject(self, vectorized_mobject):
|
def append_vectorized_mobject(self, vectorized_mobject):
|
||||||
new_points = list(vectorized_mobject.points)
|
new_points = list(vectorized_mobject.points)
|
||||||
if len(new_points) == 0:
|
|
||||||
return
|
|
||||||
if self.get_num_points() == 0:
|
|
||||||
self.start_at(new_points[0])
|
|
||||||
self.add_control_points(new_points[1:])
|
|
||||||
else:
|
|
||||||
self.add_control_points(2 * [new_points[0]] + new_points)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def get_subpath_mobjects(self):
|
if self.has_new_path_started():
|
||||||
return [m for m in self.submobjects if hasattr(m, 'is_subpath') and m.is_subpath]
|
# Remove last point, which is starting
|
||||||
|
# a new path
|
||||||
|
self.points = self.points[:-1]
|
||||||
|
self.append_points(new_points)
|
||||||
|
|
||||||
def apply_function(self, function):
|
def apply_function(self, function):
|
||||||
factor = self.pre_function_handle_to_anchor_scale_factor
|
factor = self.pre_function_handle_to_anchor_scale_factor
|
||||||
|
@ -495,95 +564,134 @@ class VMobject(Mobject):
|
||||||
again.
|
again.
|
||||||
"""
|
"""
|
||||||
for submob in self.family_members_with_points():
|
for submob in self.family_members_with_points():
|
||||||
anchors, handles1, handles2 = submob.get_anchors_and_handles()
|
a1, h1, h2, a2 = submob.get_anchors_and_handles()
|
||||||
# print len(anchors), len(handles1), len(handles2)
|
a1_to_h1 = h1 - a1
|
||||||
a_to_h1 = handles1 - anchors[:-1]
|
a2_to_h2 = h2 - a2
|
||||||
a_to_h2 = handles2 - anchors[1:]
|
new_h1 = a1 + factor * a1_to_h1
|
||||||
handles1 = anchors[:-1] + factor * a_to_h1
|
new_h2 = a2 + factor * a2_to_h2
|
||||||
handles2 = anchors[1:] + factor * a_to_h2
|
submob.set_anchors_and_handles(a1, new_h1, new_h2, a2)
|
||||||
submob.set_anchors_and_handles(anchors, handles1, handles2)
|
return self
|
||||||
|
|
||||||
|
#
|
||||||
|
def consider_points_equals(self, p0, p1):
|
||||||
|
return np.allclose(
|
||||||
|
p0, p1,
|
||||||
|
atol=self.tolerance_for_point_equality
|
||||||
|
)
|
||||||
|
|
||||||
# Information about line
|
# Information about line
|
||||||
|
|
||||||
def component_curves(self):
|
def get_all_cubic_bezier_point_tuples(self):
|
||||||
for n in range(self.get_num_anchor_points() - 1):
|
points = self.get_points()
|
||||||
yield self.get_nth_curve(n)
|
if self.has_new_path_started():
|
||||||
|
points = points[:-1]
|
||||||
|
# TODO, Throw error if len(points) is > 1 and
|
||||||
|
# not divisible by 4 (i.e. n_points_per_cubic_curve)
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
|
return np.array([
|
||||||
|
points[i:i + nppcc]
|
||||||
|
for i in range(0, len(points), nppcc)
|
||||||
|
])
|
||||||
|
|
||||||
def get_nth_curve(self, n):
|
def get_nth_curve_points(self, n):
|
||||||
return bezier(self.points[3 * n:3 * n + 4])
|
assert(n < self.get_num_curves())
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
|
return self.points[nppcc * n:nppcc * (n + 1)]
|
||||||
|
|
||||||
def get_num_anchor_points(self):
|
def get_nth_curve_function(self, n):
|
||||||
return (len(self.points) - 1) // 3 + 1
|
return bezier(self.get_nth_curve_points(n))
|
||||||
|
|
||||||
|
def get_num_curves(self):
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
|
return len(self.points) // nppcc
|
||||||
|
|
||||||
def point_from_proportion(self, alpha):
|
def point_from_proportion(self, alpha):
|
||||||
num_cubics = self.get_num_anchor_points() - 1
|
num_cubics = self.get_num_curves()
|
||||||
interpoint_alpha = num_cubics * (alpha % (1. / num_cubics))
|
n, residue = integer_interpolate(0, num_cubics, alpha)
|
||||||
index = min(3 * int(alpha * num_cubics), 3 * num_cubics)
|
curve = self.get_nth_curve_function(n)
|
||||||
cubic = bezier(self.points[index:index + 4])
|
return curve(residue)
|
||||||
return cubic(interpoint_alpha)
|
|
||||||
|
|
||||||
def get_anchors_and_handles(self):
|
def get_anchors_and_handles(self):
|
||||||
|
"""
|
||||||
|
returns anchors1, handles1, handles2, anchors2,
|
||||||
|
where (anchors1[i], handles1[i], handles2[i], anchors2[i])
|
||||||
|
will be four points defining a cubic bezier curve
|
||||||
|
for any i in range(0, len(anchors1))
|
||||||
|
"""
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
return [
|
return [
|
||||||
self.points[i::3]
|
self.points[i::nppcc]
|
||||||
for i in range(3)
|
for i in range(nppcc)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_start_anchors(self):
|
||||||
|
return self.points[0::self.n_points_per_cubic_curve]
|
||||||
|
|
||||||
|
def get_end_anchors(self):
|
||||||
|
nppcc = self.n_points_per_cubic_curve
|
||||||
|
return self.points[nppcc - 1::nppcc]
|
||||||
|
|
||||||
def get_anchors(self):
|
def get_anchors(self):
|
||||||
return self.points[::3]
|
return np.array(list(it.chain(*zip(
|
||||||
|
self.get_start_anchors(),
|
||||||
|
self.get_end_anchors(),
|
||||||
|
))))
|
||||||
|
|
||||||
def get_points_defining_boundary(self):
|
def get_points_defining_boundary(self):
|
||||||
return np.array(list(it.chain(*[
|
return np.array(list(it.chain(*[
|
||||||
sm.get_anchors() for sm in self.get_family()
|
sm.get_anchors()
|
||||||
|
for sm in self.get_family()
|
||||||
])))
|
])))
|
||||||
|
|
||||||
# Alignment
|
# Alignment
|
||||||
def align_points(self, vmobject):
|
def align_points(self, vmobject):
|
||||||
|
# This will call back to align_points_with_larger
|
||||||
Mobject.align_points(self, vmobject)
|
Mobject.align_points(self, vmobject)
|
||||||
self.align_rgbas(vmobject)
|
self.align_rgbas(vmobject)
|
||||||
is_subpath = self.is_subpath or vmobject.is_subpath
|
|
||||||
self.is_subpath = vmobject.is_subpath = is_subpath
|
|
||||||
mark_closed = self.mark_paths_closed and vmobject.mark_paths_closed
|
|
||||||
self.mark_paths_closed = vmobject.mark_paths_closed = mark_closed
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def align_points_with_larger(self, larger_mobject):
|
def align_points_with_larger(self, larger_mobject):
|
||||||
assert(isinstance(larger_mobject, VMobject))
|
assert(isinstance(larger_mobject, VMobject))
|
||||||
self.insert_n_anchor_points(
|
lnc = larger_mobject.get_num_curves()
|
||||||
larger_mobject.get_num_anchor_points() -
|
snc = self.get_num_curves()
|
||||||
self.get_num_anchor_points()
|
self.insert_n_curves(lnc - snc)
|
||||||
)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def insert_n_anchor_points(self, n):
|
def insert_n_curves(self, n):
|
||||||
curr = self.get_num_anchor_points()
|
new_path_point = None
|
||||||
if curr == 0:
|
if self.has_new_path_started():
|
||||||
self.points = np.zeros((1, 3))
|
new_path_point = self.get_last_point()
|
||||||
n = n - 1
|
curr_curve_points = self.get_all_cubic_bezier_point_tuples()
|
||||||
if curr == 1:
|
curr_num = len(curr_curve_points)
|
||||||
self.points = np.repeat(self.points, 3 * n + 1, axis=0)
|
target_num = curr_num + n
|
||||||
return self
|
# This is an array with values ranging from 0
|
||||||
points = np.array([self.points[0]])
|
# up to curr_num, with repeats such that
|
||||||
num_curves = curr - 1
|
# it's total length is target_num. For example,
|
||||||
# Curves in self are buckets, and we need to know
|
# with curr_num = 10, target_num = 15, this would
|
||||||
# how many new anchor points to put into each one.
|
# be [0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9]
|
||||||
# Each element of index_allocation is like a bucket,
|
repeat_indices = (np.arange(target_num) * curr_num) // target_num
|
||||||
# and its value tells you the appropriate index of
|
|
||||||
# the smaller curve.
|
# If the nth term of this list is k, it means
|
||||||
index_allocation = (
|
# that the nth curve of our path should be split
|
||||||
np.arange(curr + n - 1) * num_curves) // (curr + n - 1)
|
# into k pieces. In the above example, this would
|
||||||
for index in range(num_curves):
|
# be [2, 1, 2, 1, 2, 1, 2, 1, 2, 1]
|
||||||
curr_bezier_points = self.points[3 * index:3 * index + 4]
|
split_factors = [
|
||||||
num_inter_curves = sum(index_allocation == index)
|
sum(repeat_indices == i)
|
||||||
alphas = np.linspace(0, 1, num_inter_curves + 1)
|
for i in range(curr_num)
|
||||||
# alphas = np.arange(0, num_inter_curves+1)/float(num_inter_curves)
|
]
|
||||||
for a, b in zip(alphas, alphas[1:]):
|
|
||||||
new_points = partial_bezier_points(
|
self.clear_points()
|
||||||
curr_bezier_points, a, b
|
for points, sf in zip(curr_curve_points, split_factors):
|
||||||
|
# What was once a single cubic curve defined
|
||||||
|
# by "points" will now be broken into sf
|
||||||
|
# smaller cubic curves
|
||||||
|
alphas = np.linspace(0, 1, sf + 1)
|
||||||
|
for a1, a2 in zip(alphas, alphas[1:]):
|
||||||
|
self.append_points(
|
||||||
|
partial_bezier_points(points, a1, a2)
|
||||||
)
|
)
|
||||||
points = np.append(
|
if new_path_point is not None:
|
||||||
points, new_points[1:], axis=0
|
self.append_points([new_path_point])
|
||||||
)
|
|
||||||
self.set_points(points)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def align_rgbas(self, vmobject):
|
def align_rgbas(self, vmobject):
|
||||||
|
@ -604,11 +712,6 @@ class VMobject(Mobject):
|
||||||
center = self.get_center()
|
center = self.get_center()
|
||||||
return VectorizedPoint(center)
|
return VectorizedPoint(center)
|
||||||
|
|
||||||
def repeat_submobject(self, submobject):
|
|
||||||
if submobject.is_subpath:
|
|
||||||
return VectorizedPoint(submobject.points[0])
|
|
||||||
return submobject.copy()
|
|
||||||
|
|
||||||
def interpolate_color(self, mobject1, mobject2, alpha):
|
def interpolate_color(self, mobject1, mobject2, alpha):
|
||||||
attrs = [
|
attrs = [
|
||||||
"fill_rgbas",
|
"fill_rgbas",
|
||||||
|
@ -628,40 +731,40 @@ class VMobject(Mobject):
|
||||||
if alpha == 1.0:
|
if alpha == 1.0:
|
||||||
setattr(self, attr, getattr(mobject2, attr))
|
setattr(self, attr, getattr(mobject2, attr))
|
||||||
|
|
||||||
def pointwise_become_partial(self, mobject, a, b):
|
def pointwise_become_partial(self, vmobject, a, b):
|
||||||
assert(isinstance(mobject, VMobject))
|
assert(isinstance(vmobject, VMobject))
|
||||||
# Partial curve includes three portions:
|
# Partial curve includes three portions:
|
||||||
# - A middle section, which matches the curve exactly
|
# - A middle section, which matches the curve exactly
|
||||||
# - A start, which is some ending portion of an inner cubic
|
# - A start, which is some ending portion of an inner cubic
|
||||||
# - An end, which is the starting portion of a later inner cubic
|
# - An end, which is the starting portion of a later inner cubic
|
||||||
if a <= 0 and b >= 1:
|
if a <= 0 and b >= 1:
|
||||||
self.set_points(mobject.points)
|
self.set_points(vmobject.points)
|
||||||
self.mark_paths_closed = mobject.mark_paths_closed
|
|
||||||
return self
|
return self
|
||||||
self.mark_paths_closed = False
|
bezier_quads = vmobject.get_all_cubic_bezier_point_tuples()
|
||||||
num_cubics = mobject.get_num_anchor_points() - 1
|
num_cubics = len(bezier_quads)
|
||||||
lower_index = int(a * num_cubics)
|
|
||||||
upper_index = int(b * num_cubics)
|
|
||||||
points = np.array(
|
|
||||||
mobject.points[3 * lower_index:3 * upper_index + 4]
|
|
||||||
)
|
|
||||||
if len(points) > 1:
|
|
||||||
a_residue = (num_cubics * a) % 1
|
|
||||||
b_residue = (num_cubics * b) % 1
|
|
||||||
if b == 1:
|
|
||||||
b_residue = 1
|
|
||||||
elif lower_index == upper_index:
|
|
||||||
b_residue = (b_residue - a_residue) / (1 - a_residue)
|
|
||||||
|
|
||||||
points[:4] = partial_bezier_points(
|
lower_index, lower_residue = integer_interpolate(0, num_cubics, a)
|
||||||
points[:4], a_residue, 1
|
upper_index, upper_residue = integer_interpolate(0, num_cubics, b)
|
||||||
)
|
|
||||||
points[-4:] = partial_bezier_points(
|
self.clear_points()
|
||||||
points[-4:], 0, b_residue
|
self.append_points(partial_bezier_points(
|
||||||
)
|
bezier_quads[lower_index], lower_residue, 1
|
||||||
self.set_points(points)
|
))
|
||||||
|
for quad in bezier_quads[lower_index + 1:upper_index]:
|
||||||
|
self.append_points(quad)
|
||||||
|
self.append_points(partial_bezier_points(
|
||||||
|
bezier_quads[upper_index], 0, upper_residue
|
||||||
|
))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
# Errors
|
||||||
|
def throw_error_if_no_points(self):
|
||||||
|
if self.has_no_points():
|
||||||
|
message = "Cannot call VMobject.{}" +\
|
||||||
|
"for a VMobject with no points"
|
||||||
|
caller_name = sys._getframe(1).f_code.co_name
|
||||||
|
raise Exception(message.format(caller_name))
|
||||||
|
|
||||||
|
|
||||||
class VGroup(VMobject):
|
class VGroup(VMobject):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
@ -292,12 +292,13 @@ class CircularFractal(SelfSimilarFractal):
|
||||||
|
|
||||||
|
|
||||||
class JaggedCurvePiece(VMobject):
|
class JaggedCurvePiece(VMobject):
|
||||||
def insert_n_anchor_points(self, n):
|
def insert_n_curves(self, n):
|
||||||
if self.get_num_anchor_points() == 0:
|
if self.get_num_curves() == 0:
|
||||||
self.points = np.zeros((1, 3))
|
self.set_points(np.zeros((1, 3)))
|
||||||
anchors = self.get_anchors()
|
anchors = self.get_anchors()
|
||||||
indices = np.linspace(0, len(anchors) - 1, n + len(anchors)) \
|
indices = np.linspace(
|
||||||
.astype('int')
|
0, len(anchors) - 1, n + len(anchors)
|
||||||
|
).astype('int')
|
||||||
self.set_points_as_corners(anchors[indices])
|
self.set_points_as_corners(anchors[indices])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
from scipy import linalg
|
from scipy import linalg
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from manimlib.utils.simple_functions import choose_using_cache
|
from manimlib.utils.simple_functions import choose
|
||||||
from manimlib.utils.space_ops import get_norm
|
|
||||||
|
|
||||||
CLOSED_THRESHOLD = 0.001
|
CLOSED_THRESHOLD = 0.001
|
||||||
|
|
||||||
|
@ -10,7 +9,7 @@ CLOSED_THRESHOLD = 0.001
|
||||||
def bezier(points):
|
def bezier(points):
|
||||||
n = len(points) - 1
|
n = len(points) - 1
|
||||||
return lambda t: sum([
|
return lambda t: sum([
|
||||||
((1 - t)**(n - k)) * (t**k) * choose_using_cache(n, k) * point
|
((1 - t)**(n - k)) * (t**k) * choose(n, k) * point
|
||||||
for k, point in enumerate(points)
|
for k, point in enumerate(points)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -41,6 +40,25 @@ def interpolate(start, end, alpha):
|
||||||
return (1 - alpha) * start + alpha * end
|
return (1 - alpha) * start + alpha * end
|
||||||
|
|
||||||
|
|
||||||
|
def integer_interpolate(start, end, alpha):
|
||||||
|
"""
|
||||||
|
alpha is a float between 0 and 1. This returns
|
||||||
|
an integer between start and end (inclusive) representing
|
||||||
|
appropriate interpolation between them, along with a
|
||||||
|
"residue" representing a new proportion between the
|
||||||
|
returned integer and the next one of the
|
||||||
|
list.
|
||||||
|
|
||||||
|
For example, if start=0, end=10, alpha=0.46, This
|
||||||
|
would return (4, 0.6).
|
||||||
|
"""
|
||||||
|
if alpha >= 1:
|
||||||
|
return (end - 1, 1.0)
|
||||||
|
value = int(interpolate(start, end, alpha))
|
||||||
|
residue = ((end - start) * alpha) % 1
|
||||||
|
return (value, residue)
|
||||||
|
|
||||||
|
|
||||||
def mid(start, end):
|
def mid(start, end):
|
||||||
return (start + end) / 2.0
|
return (start + end) / 2.0
|
||||||
|
|
||||||
|
@ -134,4 +152,4 @@ def diag_to_matrix(l_and_u, diag):
|
||||||
|
|
||||||
|
|
||||||
def is_closed(points):
|
def is_closed(points):
|
||||||
return get_norm(points[0] - points[-1]) < CLOSED_THRESHOLD
|
return np.allclose(points[0], points[-1])
|
||||||
|
|
|
@ -15,11 +15,13 @@ def choose_using_cache(n, r):
|
||||||
if n not in CHOOSE_CACHE:
|
if n not in CHOOSE_CACHE:
|
||||||
CHOOSE_CACHE[n] = {}
|
CHOOSE_CACHE[n] = {}
|
||||||
if r not in CHOOSE_CACHE[n]:
|
if r not in CHOOSE_CACHE[n]:
|
||||||
CHOOSE_CACHE[n][r] = choose(n, r)
|
CHOOSE_CACHE[n][r] = choose(n, r, use_cache=False)
|
||||||
return CHOOSE_CACHE[n][r]
|
return CHOOSE_CACHE[n][r]
|
||||||
|
|
||||||
|
|
||||||
def choose(n, r):
|
def choose(n, r, use_cache=True):
|
||||||
|
if use_cache:
|
||||||
|
return choose_using_cache(n, r)
|
||||||
if n < r:
|
if n < r:
|
||||||
return 0
|
return 0
|
||||||
if r == 0:
|
if r == 0:
|
||||||
|
|
|
@ -1763,7 +1763,7 @@ class Initial2dFuncSceneWithoutMorphing(Initial2dFuncSceneBase):
|
||||||
# Alternative to the above, manually implementing split screen with a morphing animation
|
# Alternative to the above, manually implementing split screen with a morphing animation
|
||||||
class Initial2dFuncSceneMorphing(Initial2dFuncSceneBase):
|
class Initial2dFuncSceneMorphing(Initial2dFuncSceneBase):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"num_needed_anchor_points" : 10,
|
"num_needed_anchor_curves" : 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
@ -1781,8 +1781,8 @@ class Initial2dFuncSceneMorphing(Initial2dFuncSceneBase):
|
||||||
|
|
||||||
def obj_draw(self, input_object):
|
def obj_draw(self, input_object):
|
||||||
output_object = input_object.copy()
|
output_object = input_object.copy()
|
||||||
if input_object.get_num_anchor_points() < self.num_needed_anchor_points:
|
if input_object.get_num_curves() < self.num_needed_anchor_curves:
|
||||||
input_object.insert_n_anchor_points(self.num_needed_anchor_points)
|
input_object.insert_n_curves(self.num_needed_anchor_curves)
|
||||||
output_object.apply_function(self.func)
|
output_object.apply_function(self.func)
|
||||||
self.squash_onto_left(input_object)
|
self.squash_onto_left(input_object)
|
||||||
self.squash_onto_right(output_object)
|
self.squash_onto_right(output_object)
|
||||||
|
|
|
@ -1606,7 +1606,7 @@ class DirectionOfA2DFunctionAlongABoundary(InputOutputScene):
|
||||||
input_plane.coords_to_point(2.5, -1.5),
|
input_plane.coords_to_point(2.5, -1.5),
|
||||||
)
|
)
|
||||||
rect.replace(line, stretch = True)
|
rect.replace(line, stretch = True)
|
||||||
rect.insert_n_anchor_points(50)
|
rect.insert_n_curves(50)
|
||||||
rect.match_background_image_file(colorings[0])
|
rect.match_background_image_file(colorings[0])
|
||||||
|
|
||||||
rect_image = rect.copy()
|
rect_image = rect.copy()
|
||||||
|
@ -1815,7 +1815,7 @@ class ForeverNarrowingLoop(InputOutputScene):
|
||||||
# circle
|
# circle
|
||||||
circle = Circle(color = WHITE, radius = self.circle_start_radius)
|
circle = Circle(color = WHITE, radius = self.circle_start_radius)
|
||||||
circle.flip(axis = RIGHT)
|
circle.flip(axis = RIGHT)
|
||||||
circle.insert_n_anchor_points(50)
|
circle.insert_n_curves(50)
|
||||||
if self.start_around_target:
|
if self.start_around_target:
|
||||||
circle.move_to(input_plane.coords_to_point(*self.target_coords))
|
circle.move_to(input_plane.coords_to_point(*self.target_coords))
|
||||||
else:
|
else:
|
||||||
|
@ -2788,7 +2788,7 @@ class WindingNumbersInInputOutputContext(PathContainingZero):
|
||||||
in_loop = Circle()
|
in_loop = Circle()
|
||||||
in_loop.flip(RIGHT)
|
in_loop.flip(RIGHT)
|
||||||
# in_loop = Square(side_length = 2)
|
# in_loop = Square(side_length = 2)
|
||||||
in_loop.insert_n_anchor_points(100)
|
in_loop.insert_n_curves(100)
|
||||||
in_loop.move_to(self.input_plane.coords_to_point(
|
in_loop.move_to(self.input_plane.coords_to_point(
|
||||||
*self.in_loop_center_coords
|
*self.in_loop_center_coords
|
||||||
))
|
))
|
||||||
|
|
|
@ -45,7 +45,7 @@ class NumberlineTransformationScene(ZoomedScene):
|
||||||
"color": BLUE,
|
"color": BLUE,
|
||||||
},
|
},
|
||||||
"output_line_config": {},
|
"output_line_config": {},
|
||||||
"num_inserted_number_line_anchors": 20,
|
"num_inserted_number_line_curves": 20,
|
||||||
"default_delta_x": 0.1,
|
"default_delta_x": 0.1,
|
||||||
"default_sample_dot_radius": 0.07,
|
"default_sample_dot_radius": 0.07,
|
||||||
"default_sample_dot_colors": [RED, YELLOW],
|
"default_sample_dot_colors": [RED, YELLOW],
|
||||||
|
@ -76,8 +76,8 @@ class NumberlineTransformationScene(ZoomedScene):
|
||||||
full_config = dict(self.number_line_config)
|
full_config = dict(self.number_line_config)
|
||||||
full_config.update(added_config)
|
full_config.update(added_config)
|
||||||
number_line = NumberLine(**full_config)
|
number_line = NumberLine(**full_config)
|
||||||
number_line.main_line.insert_n_anchor_points(
|
number_line.main_line.insert_n_curves(
|
||||||
self.num_inserted_number_line_anchors
|
self.num_inserted_number_line_curves
|
||||||
)
|
)
|
||||||
number_line.shift(zero_point - number_line.number_to_point(0))
|
number_line.shift(zero_point - number_line.number_to_point(0))
|
||||||
number_lines.add(number_line)
|
number_lines.add(number_line)
|
||||||
|
@ -179,8 +179,8 @@ class NumberlineTransformationScene(ZoomedScene):
|
||||||
self.moving_input_line = input_line_copy
|
self.moving_input_line = input_line_copy
|
||||||
input_line_copy.remove(input_line_copy.numbers)
|
input_line_copy.remove(input_line_copy.numbers)
|
||||||
# input_line_copy.set_stroke(width=2)
|
# input_line_copy.set_stroke(width=2)
|
||||||
input_line_copy.main_line.insert_n_anchor_points(
|
input_line_copy.main_line.insert_n_curves(
|
||||||
self.num_inserted_number_line_anchors
|
self.num_inserted_number_line_curves
|
||||||
)
|
)
|
||||||
return AnimationGroup(
|
return AnimationGroup(
|
||||||
self.get_mapping_animation(
|
self.get_mapping_animation(
|
||||||
|
@ -311,7 +311,7 @@ class NumberlineTransformationScene(ZoomedScene):
|
||||||
# Add miniature number_line
|
# Add miniature number_line
|
||||||
mini_line = self.mini_line = Line(frame.get_left(), frame.get_right())
|
mini_line = self.mini_line = Line(frame.get_left(), frame.get_right())
|
||||||
mini_line.scale(self.mini_line_scale_factor)
|
mini_line.scale(self.mini_line_scale_factor)
|
||||||
mini_line.insert_n_anchor_points(self.num_inserted_number_line_anchors)
|
mini_line.insert_n_curves(self.num_inserted_number_line_curves)
|
||||||
mini_line.match_style(self.input_line.main_line)
|
mini_line.match_style(self.input_line.main_line)
|
||||||
mini_line_copy = mini_line.copy()
|
mini_line_copy = mini_line.copy()
|
||||||
zcbr_group.add(mini_line_copy, mini_line)
|
zcbr_group.add(mini_line_copy, mini_line)
|
||||||
|
|
|
@ -1523,7 +1523,7 @@ class ShowLightInThreeDimensions(IntroduceScreen, ThreeDScene):
|
||||||
screens = VGroup(
|
screens = VGroup(
|
||||||
Square(),
|
Square(),
|
||||||
RegularPolygon(8),
|
RegularPolygon(8),
|
||||||
Circle().insert_n_anchor_points(25),
|
Circle().insert_n_curves(25),
|
||||||
)
|
)
|
||||||
for screen in screens:
|
for screen in screens:
|
||||||
screen.set_height(self.screen_height)
|
screen.set_height(self.screen_height)
|
||||||
|
@ -3883,7 +3883,7 @@ class ThinkBackToHowAmazingThisIs(ThreeDScene):
|
||||||
self.number_line = number_line
|
self.number_line = number_line
|
||||||
|
|
||||||
def show_giant_circle(self):
|
def show_giant_circle(self):
|
||||||
self.number_line.main_line.insert_n_anchor_points(10000)
|
self.number_line.main_line.insert_n_curves(10000)
|
||||||
everything = VGroup(*self.mobjects)
|
everything = VGroup(*self.mobjects)
|
||||||
circle = everything.copy()
|
circle = everything.copy()
|
||||||
circle.move_to(ORIGIN)
|
circle.move_to(ORIGIN)
|
||||||
|
|
|
@ -2635,7 +2635,7 @@ class Test(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
randy = Randolph()
|
randy = Randolph()
|
||||||
necklace = Necklace()
|
necklace = Necklace()
|
||||||
necklace.insert_n_anchor_points(20)
|
necklace.insert_n_curves(20)
|
||||||
# necklace.apply_function(
|
# necklace.apply_function(
|
||||||
# lambda (x, y, z) : x*RIGHT + (y + 0.1*x**2)*UP
|
# lambda (x, y, z) : x*RIGHT + (y + 0.1*x**2)*UP
|
||||||
# )
|
# )
|
||||||
|
|
|
@ -843,7 +843,7 @@ class CylinderModel(Scene):
|
||||||
shift_val = 0.1 * LEFT + 0.2 * UP
|
shift_val = 0.1 * LEFT + 0.2 * UP
|
||||||
scale_factor = get_norm(RIGHT - shift_val)
|
scale_factor = get_norm(RIGHT - shift_val)
|
||||||
movers = VGroup(self.warped_grid, self.unit_circle)
|
movers = VGroup(self.warped_grid, self.unit_circle)
|
||||||
self.unit_circle.insert_n_anchor_points(50)
|
self.unit_circle.insert_n_curves(50)
|
||||||
|
|
||||||
stream_lines = self.get_stream_lines()
|
stream_lines = self.get_stream_lines()
|
||||||
stream_lines.scale(scale_factor)
|
stream_lines.scale(scale_factor)
|
||||||
|
@ -4493,7 +4493,7 @@ class BroughtToYouBy(PiCreatureScene):
|
||||||
math.move_to(self.pi_creatures)
|
math.move_to(self.pi_creatures)
|
||||||
|
|
||||||
spiral = Line(0.5 * RIGHT, 0.5 * RIGHT + 70 * UP)
|
spiral = Line(0.5 * RIGHT, 0.5 * RIGHT + 70 * UP)
|
||||||
spiral.insert_n_anchor_points(1000)
|
spiral.insert_n_curves(1000)
|
||||||
from old_projects.zeta import zeta
|
from old_projects.zeta import zeta
|
||||||
spiral.apply_complex_function(zeta)
|
spiral.apply_complex_function(zeta)
|
||||||
step = 0.1
|
step = 0.1
|
||||||
|
|
|
@ -2964,7 +2964,7 @@ class ComplexExponentiationAdderHalf(
|
||||||
)
|
)
|
||||||
line.set_color(YELLOW)
|
line.set_color(YELLOW)
|
||||||
for submob in line:
|
for submob in line:
|
||||||
submob.insert_n_anchor_points(10)
|
submob.insert_n_curves(10)
|
||||||
submob.make_smooth()
|
submob.make_smooth()
|
||||||
circle = VGroup(
|
circle = VGroup(
|
||||||
Circle(),
|
Circle(),
|
||||||
|
@ -3069,7 +3069,7 @@ class ComplexExponentiationMultiplierHalf(
|
||||||
line.set_color(YELLOW)
|
line.set_color(YELLOW)
|
||||||
line.shift(FRAME_X_RADIUS*LEFT)
|
line.shift(FRAME_X_RADIUS*LEFT)
|
||||||
for submob in line:
|
for submob in line:
|
||||||
submob.insert_n_anchor_points(10)
|
submob.insert_n_curves(10)
|
||||||
submob.make_smooth()
|
submob.make_smooth()
|
||||||
circle = VGroup(
|
circle = VGroup(
|
||||||
Circle(),
|
Circle(),
|
||||||
|
@ -3110,7 +3110,7 @@ class ComplexExponentiationMultiplierHalf(
|
||||||
arc_line = Line(RIGHT, RIGHT+angle*UP)
|
arc_line = Line(RIGHT, RIGHT+angle*UP)
|
||||||
brace = Brace(arc_line, RIGHT, buff = 0)
|
brace = Brace(arc_line, RIGHT, buff = 0)
|
||||||
for submob in brace.family_members_with_points():
|
for submob in brace.family_members_with_points():
|
||||||
submob.insert_n_anchor_points(10)
|
submob.insert_n_curves(10)
|
||||||
curved_brace = brace.copy()
|
curved_brace = brace.copy()
|
||||||
curved_brace.shift(LEFT)
|
curved_brace.shift(LEFT)
|
||||||
curved_brace.apply_complex_function(
|
curved_brace.apply_complex_function(
|
||||||
|
|
|
@ -162,7 +162,7 @@ class CircleScene(PiCreatureScene):
|
||||||
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
|
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
|
||||||
R = ring.R
|
R = ring.R
|
||||||
R_plus_dr = ring.R + ring.dR
|
R_plus_dr = ring.R + ring.dR
|
||||||
n_anchors = ring.get_num_anchor_points()
|
n_anchors = ring.get_num_curves()
|
||||||
result = VMobject()
|
result = VMobject()
|
||||||
result.set_points_as_corners([
|
result.set_points_as_corners([
|
||||||
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
|
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
|
||||||
|
@ -1115,7 +1115,7 @@ class GraphRectangles(CircleScene, GraphScene):
|
||||||
|
|
||||||
ring.rect = rect
|
ring.rect = rect
|
||||||
|
|
||||||
n_anchors = ring.get_num_anchor_points()
|
n_anchors = ring.get_num_curves()
|
||||||
target = VMobject()
|
target = VMobject()
|
||||||
target.set_points_as_corners([
|
target.set_points_as_corners([
|
||||||
interpolate(ORIGIN, DOWN, a)
|
interpolate(ORIGIN, DOWN, a)
|
||||||
|
|
|
@ -2218,7 +2218,7 @@ class IntroduceUnitCircleWithSine(GraphScene):
|
||||||
color = YELLOW,
|
color = YELLOW,
|
||||||
)
|
)
|
||||||
line.shift(FRAME_X_RADIUS*RIGHT/3).to_edge(UP)
|
line.shift(FRAME_X_RADIUS*RIGHT/3).to_edge(UP)
|
||||||
line.insert_n_anchor_points(10)
|
line.insert_n_curves(10)
|
||||||
line.make_smooth()
|
line.make_smooth()
|
||||||
|
|
||||||
arc = Arc(
|
arc = Arc(
|
||||||
|
|
|
@ -1114,7 +1114,7 @@ class WhyPi(PiCreatureScene):
|
||||||
circum.set_color(circle.get_color())
|
circum.set_color(circle.get_color())
|
||||||
circum.scale(np.pi)
|
circum.scale(np.pi)
|
||||||
circum.next_to(circle, DOWN, LARGE_BUFF)
|
circum.next_to(circle, DOWN, LARGE_BUFF)
|
||||||
circum.insert_n_anchor_points(circle.get_num_anchor_points()-2)
|
circum.insert_n_curves(circle.get_num_curves()-2)
|
||||||
circum.make_jagged()
|
circum.make_jagged()
|
||||||
pi = TexMobject("\\pi")
|
pi = TexMobject("\\pi")
|
||||||
pi.next_to(circum, UP)
|
pi.next_to(circum, UP)
|
||||||
|
|
|
@ -156,7 +156,7 @@ class CircleScene(PiCreatureScene):
|
||||||
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
|
def get_unwrapped(self, ring, to_edge = LEFT, **kwargs):
|
||||||
R = ring.R
|
R = ring.R
|
||||||
R_plus_dr = ring.R + ring.dR
|
R_plus_dr = ring.R + ring.dR
|
||||||
n_anchors = ring.get_num_anchor_points()
|
n_anchors = ring.get_num_curves()
|
||||||
result = VMobject()
|
result = VMobject()
|
||||||
result.set_points_as_corners([
|
result.set_points_as_corners([
|
||||||
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
|
interpolate(np.pi*R_plus_dr*LEFT, np.pi*R_plus_dr*RIGHT, a)
|
||||||
|
|
|
@ -500,7 +500,7 @@ class SneakyNonlinearTransformationExplained(SneakyNonlinearTransformation):
|
||||||
FRAME_Y_RADIUS*LEFT+FRAME_Y_RADIUS*DOWN,
|
FRAME_Y_RADIUS*LEFT+FRAME_Y_RADIUS*DOWN,
|
||||||
FRAME_Y_RADIUS*RIGHT + FRAME_Y_RADIUS*UP
|
FRAME_Y_RADIUS*RIGHT + FRAME_Y_RADIUS*UP
|
||||||
)
|
)
|
||||||
diag.insert_n_anchor_points(20)
|
diag.insert_n_curves(20)
|
||||||
diag.change_anchor_mode("smooth")
|
diag.change_anchor_mode("smooth")
|
||||||
diag.set_color(YELLOW)
|
diag.set_color(YELLOW)
|
||||||
self.play(ShowCreation(diag))
|
self.play(ShowCreation(diag))
|
||||||
|
|
|
@ -2794,7 +2794,7 @@ class WriteComplexExponentialExpression(DrawFrequencyPlot):
|
||||||
for t in (get_t(), TAU)
|
for t in (get_t(), TAU)
|
||||||
]
|
]
|
||||||
for mob in arc, circle:
|
for mob in arc, circle:
|
||||||
mob.insert_n_anchor_points(20)
|
mob.insert_n_curves(20)
|
||||||
mob.set_stroke(RED, 4)
|
mob.set_stroke(RED, 4)
|
||||||
mob.apply_function(
|
mob.apply_function(
|
||||||
lambda p : plane.number_to_point(
|
lambda p : plane.number_to_point(
|
||||||
|
|
|
@ -1442,8 +1442,8 @@ class DimensionOfQuadraticKoch(DimensionOfKoch):
|
||||||
for order in range(2, self.koch_curve_order+1):
|
for order in range(2, self.koch_curve_order+1):
|
||||||
new_curve = self.get_curve(order)
|
new_curve = self.get_curve(order)
|
||||||
new_curve.move_to(curve)
|
new_curve.move_to(curve)
|
||||||
n_anchors = len(curve.get_anchors())
|
n_curve_parts = curve.get_num_curves()
|
||||||
curve.insert_n_anchor_points(6*n_anchors)
|
curve.insert_n_curves(6 * n_curve_parts)
|
||||||
curve.make_jagged()
|
curve.make_jagged()
|
||||||
self.play(Transform(curve, new_curve, run_time = 2))
|
self.play(Transform(curve, new_curve, run_time = 2))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
|
@ -483,7 +483,7 @@ class EulerWrites628(Scene):
|
||||||
fill_opacity = 0.3,
|
fill_opacity = 0.3,
|
||||||
fill_color = GREEN,
|
fill_color = GREEN,
|
||||||
)
|
)
|
||||||
rect.insert_n_anchor_points(20)
|
rect.insert_n_curves(20)
|
||||||
rect.apply_function(lambda p : np.array([p[0], p[1] - 0.005*p[0]**2, p[2]]))
|
rect.apply_function(lambda p : np.array([p[0], p[1] - 0.005*p[0]**2, p[2]]))
|
||||||
rect.rotate(0.012*TAU)
|
rect.rotate(0.012*TAU)
|
||||||
rect.move_to(image)
|
rect.move_to(image)
|
||||||
|
|
|
@ -3206,7 +3206,7 @@ class SecondProof(SpecialThreeDScene):
|
||||||
ring.set_fill(color, opacity=1)
|
ring.set_fill(color, opacity=1)
|
||||||
ring.set_stroke(color, width=0.5, opacity=1)
|
ring.set_stroke(color, width=0.5, opacity=1)
|
||||||
for piece in ring:
|
for piece in ring:
|
||||||
piece.insert_n_anchor_points(4)
|
piece.insert_n_curves(4)
|
||||||
piece.on_sphere = True
|
piece.on_sphere = True
|
||||||
piece.points = np.array([
|
piece.points = np.array([
|
||||||
*piece.points[3:-1],
|
*piece.points[3:-1],
|
||||||
|
|
|
@ -134,7 +134,7 @@ class Chaos(Eddy):
|
||||||
y * UP, y * UP + self.width * RIGHT,
|
y * UP, y * UP + self.width * RIGHT,
|
||||||
stroke_width=1
|
stroke_width=1
|
||||||
)
|
)
|
||||||
line.insert_n_anchor_points(self.n_midpoints)
|
line.insert_n_curves(self.n_midpoints)
|
||||||
line.total_time = random.random()
|
line.total_time = random.random()
|
||||||
delta_h = self.height / (self.n_lines - 1)
|
delta_h = self.height / (self.n_lines - 1)
|
||||||
|
|
||||||
|
@ -876,7 +876,7 @@ class HighCurlFieldBreakingLayersLines(HighCurlFieldBreakingLayers):
|
||||||
|
|
||||||
def get_line(self):
|
def get_line(self):
|
||||||
line = Line(LEFT, RIGHT)
|
line = Line(LEFT, RIGHT)
|
||||||
line.insert_n_anchor_points(500)
|
line.insert_n_curves(500)
|
||||||
line.set_width(5)
|
line.set_width(5)
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
|
@ -2039,7 +2039,7 @@ class IntroduceDopplerRadar(Scene):
|
||||||
pulse_graph.underlying_function(x),
|
pulse_graph.underlying_function(x),
|
||||||
echo_graph.underlying_function(x),
|
echo_graph.underlying_function(x),
|
||||||
]),
|
]),
|
||||||
num_graph_points = echo_graph.get_num_anchor_points(),
|
num_graph_points = echo_graph.get_num_curves(),
|
||||||
color = WHITE
|
color = WHITE
|
||||||
)
|
)
|
||||||
sum_graph.background_image_file = "blue_yellow_gradient"
|
sum_graph.background_image_file = "blue_yellow_gradient"
|
||||||
|
|
|
@ -984,7 +984,7 @@ class DeformToInterval(ClosedLoopScene):
|
||||||
interval.shift(2*DOWN)
|
interval.shift(2*DOWN)
|
||||||
numbers = interval.get_number_mobjects(0, 1)
|
numbers = interval.get_number_mobjects(0, 1)
|
||||||
line = Line(interval.get_left(), interval.get_right())
|
line = Line(interval.get_left(), interval.get_right())
|
||||||
line.insert_n_anchor_points(self.loop.get_num_anchor_points())
|
line.insert_n_curves(self.loop.get_num_curves())
|
||||||
line.make_smooth()
|
line.make_smooth()
|
||||||
|
|
||||||
self.loop.scale(0.7)
|
self.loop.scale(0.7)
|
||||||
|
@ -1058,7 +1058,7 @@ class RepresentPairInUnitSquare(ClosedLoopScene):
|
||||||
interval.shift(LEFT)
|
interval.shift(LEFT)
|
||||||
numbers = interval.get_number_mobjects(0, 1)
|
numbers = interval.get_number_mobjects(0, 1)
|
||||||
line = Line(interval.get_left(), interval.get_right())
|
line = Line(interval.get_left(), interval.get_right())
|
||||||
line.insert_n_anchor_points(self.loop.get_num_anchor_points())
|
line.insert_n_curves(self.loop.get_num_curves())
|
||||||
line.make_smooth()
|
line.make_smooth()
|
||||||
vert_interval = interval.copy()
|
vert_interval = interval.copy()
|
||||||
square = Square()
|
square = Square()
|
||||||
|
@ -1270,7 +1270,7 @@ class EndpointsGluedTogether(ClosedLoopScene):
|
||||||
interval.shift(2*DOWN)
|
interval.shift(2*DOWN)
|
||||||
numbers = interval.get_number_mobjects(0, 1)
|
numbers = interval.get_number_mobjects(0, 1)
|
||||||
line = Line(interval.get_left(), interval.get_right())
|
line = Line(interval.get_left(), interval.get_right())
|
||||||
line.insert_n_anchor_points(self.loop.get_num_anchor_points())
|
line.insert_n_curves(self.loop.get_num_curves())
|
||||||
line.make_smooth()
|
line.make_smooth()
|
||||||
|
|
||||||
self.loop.scale(0.7)
|
self.loop.scale(0.7)
|
||||||
|
|
|
@ -37,7 +37,7 @@ class ZetaTransformationScene(ComplexTransformationScene):
|
||||||
for line in mob.family_members_with_points():
|
for line in mob.family_members_with_points():
|
||||||
#Find point of line cloest to 1 on C
|
#Find point of line cloest to 1 on C
|
||||||
if not isinstance(line, Line):
|
if not isinstance(line, Line):
|
||||||
line.insert_n_anchor_points(self.min_added_anchors)
|
line.insert_n_curves(self.min_added_anchors)
|
||||||
continue
|
continue
|
||||||
p1 = line.get_start()+LEFT
|
p1 = line.get_start()+LEFT
|
||||||
p2 = line.get_end()+LEFT
|
p2 = line.get_end()+LEFT
|
||||||
|
@ -47,14 +47,14 @@ class ZetaTransformationScene(ComplexTransformationScene):
|
||||||
)
|
)
|
||||||
#See how big this line will become
|
#See how big this line will become
|
||||||
diameter = abs(zeta(complex(*closest_to_one[:2])))
|
diameter = abs(zeta(complex(*closest_to_one[:2])))
|
||||||
target_num_anchors = np.clip(
|
target_num_curves = np.clip(
|
||||||
int(self.anchor_density*np.pi*diameter),
|
int(self.anchor_density*np.pi*diameter),
|
||||||
self.min_added_anchors,
|
self.min_added_anchors,
|
||||||
self.max_added_anchors,
|
self.max_added_anchors,
|
||||||
)
|
)
|
||||||
num_anchors = line.get_num_anchor_points()
|
num_curves = line.get_num_curves()
|
||||||
if num_anchors < target_num_anchors:
|
if num_curves < target_num_curves:
|
||||||
line.insert_n_anchor_points(target_num_anchors-num_anchors)
|
line.insert_n_curves(target_num_curves-num_curves)
|
||||||
line.make_smooth()
|
line.make_smooth()
|
||||||
|
|
||||||
def add_extra_plane_lines_for_zeta(self, animate = False, **kwargs):
|
def add_extra_plane_lines_for_zeta(self, animate = False, **kwargs):
|
||||||
|
@ -2279,7 +2279,7 @@ class IntroduceAnglePreservation(VisualizingSSquared):
|
||||||
color = YELLOW
|
color = YELLOW
|
||||||
)
|
)
|
||||||
arc.shift(intersection_point)
|
arc.shift(intersection_point)
|
||||||
arc.insert_n_anchor_points(10)
|
arc.insert_n_curves(10)
|
||||||
arc.generate_target()
|
arc.generate_target()
|
||||||
input_z = complex(*arc.get_center()[:2])
|
input_z = complex(*arc.get_center()[:2])
|
||||||
scale_factor = abs(2*input_z)
|
scale_factor = abs(2*input_z)
|
||||||
|
|
Loading…
Add table
Reference in a new issue