mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
1063 lines
32 KiB
Python
1063 lines
32 KiB
Python
from manimlib.imports import *
|
|
import displayer as disp
|
|
from hilbert.curves import \
|
|
TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \
|
|
SnakeCurve, PeanoCurve
|
|
from hilbert.section1 import get_mathy_and_bubble
|
|
from scipy.spatial.distance import cdist
|
|
|
|
|
|
def get_time_line():
|
|
length = 2.6*FRAME_WIDTH
|
|
year_range = 400
|
|
time_line = NumberLine(
|
|
numerical_radius = year_range/2,
|
|
unit_length_to_spatial_width = length/year_range,
|
|
tick_frequency = 10,
|
|
leftmost_tick = 1720,
|
|
number_at_center = 1870,
|
|
numbers_with_elongated_ticks = list(range(1700, 2100, 100))
|
|
)
|
|
time_line.sort_points(lambda p : p[0])
|
|
time_line.set_color_by_gradient(
|
|
PeanoCurve.CONFIG["start_color"],
|
|
PeanoCurve.CONFIG["end_color"]
|
|
)
|
|
time_line.add_numbers(
|
|
2020, *list(range(1800, 2050, 50))
|
|
)
|
|
return time_line
|
|
|
|
|
|
class SectionTwo(Scene):
|
|
def construct(self):
|
|
self.add(TextMobject("Section 2: Filling space"))
|
|
self.wait()
|
|
|
|
class HilbertCurveIsPerfect(Scene):
|
|
def construct(self):
|
|
curve = HilbertCurve(order = 6)
|
|
curve.set_color(WHITE)
|
|
colored_curve = curve.copy()
|
|
colored_curve.thin_out(3)
|
|
lion = ImageMobject("lion", invert = False)
|
|
lion.replace(curve, stretch = True)
|
|
sparce_lion = lion.copy()
|
|
sparce_lion.thin_out(100)
|
|
distance_matrix = cdist(colored_curve.points, sparce_lion.points)
|
|
closest_point_indices = np.apply_along_axis(
|
|
np.argmin, 1, distance_matrix
|
|
)
|
|
colored_curve.rgbas = sparce_lion.rgbas[closest_point_indices]
|
|
line = Line(5*LEFT, 5*RIGHT)
|
|
Mobject.align_data(line, colored_curve)
|
|
line.rgbas = colored_curve.rgbas
|
|
|
|
self.add(lion)
|
|
self.play(ShowCreation(curve, run_time = 3))
|
|
self.play(
|
|
FadeOut(lion),
|
|
Transform(curve, colored_curve),
|
|
run_time = 3
|
|
)
|
|
self.wait()
|
|
self.play(Transform(curve, line, run_time = 5))
|
|
self.wait()
|
|
|
|
|
|
class AskMathematicianFriend(Scene):
|
|
def construct(self):
|
|
mathy, bubble = get_mathy_and_bubble()
|
|
bubble.sort_points(lambda p : np.dot(p, UP+RIGHT))
|
|
|
|
self.add(mathy)
|
|
self.wait()
|
|
self.play(ApplyMethod(
|
|
mathy.blink,
|
|
rate_func = squish_rate_func(there_and_back)
|
|
))
|
|
self.wait()
|
|
self.play(ShowCreation(bubble))
|
|
self.wait()
|
|
self.play(
|
|
ApplyMethod(mathy.shift, 3*(DOWN+LEFT)),
|
|
ApplyPointwiseFunction(
|
|
lambda p : 15*p/get_norm(p),
|
|
bubble
|
|
),
|
|
run_time = 3
|
|
)
|
|
|
|
class TimeLineAboutSpaceFilling(Scene):
|
|
def construct(self):
|
|
curve = PeanoCurve(order = 5)
|
|
curve.stretch_to_fit_width(FRAME_WIDTH)
|
|
curve.stretch_to_fit_height(FRAME_HEIGHT)
|
|
curve_start = curve.copy()
|
|
curve_start.apply_over_attr_arrays(
|
|
lambda arr : arr[:200]
|
|
)
|
|
time_line = get_time_line()
|
|
time_line.shift(-time_line.number_to_point(2000))
|
|
|
|
self.add(time_line)
|
|
self.play(ApplyMethod(
|
|
time_line.shift,
|
|
-time_line.number_to_point(1900),
|
|
run_time = 3
|
|
))
|
|
brace = Brace(
|
|
Mobject(
|
|
Point(time_line.number_to_point(1865)),
|
|
Point(time_line.number_to_point(1888)),
|
|
),
|
|
UP
|
|
)
|
|
words = TextMobject("""
|
|
Cantor drives himself (and the \\\\
|
|
mathematical community at large) \\\\
|
|
crazy with research on infinity.
|
|
""")
|
|
words.next_to(brace, UP)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
ShimmerIn(words)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
Transform(time_line, curve_start),
|
|
FadeOut(brace),
|
|
FadeOut(words)
|
|
)
|
|
self.play(ShowCreation(
|
|
curve,
|
|
run_time = 5,
|
|
rate_func=linear
|
|
))
|
|
self.wait()
|
|
|
|
|
|
|
|
class NotPixelatedSpace(Scene):
|
|
def construct(self):
|
|
grid = Grid(64, 64)
|
|
space_region = Region()
|
|
space_mobject = MobjectFromRegion(space_region, DARK_GREY)
|
|
curve = PeanoCurve(order = 5).replace(space_mobject)
|
|
line = Line(5*LEFT, 5*RIGHT)
|
|
line.set_color_by_gradient(curve.start_color, curve.end_color)
|
|
for mob in grid, space_mobject:
|
|
mob.sort_points(get_norm)
|
|
infinitely = TextMobject("Infinitely")
|
|
detailed = TextMobject("detailed")
|
|
extending = TextMobject("extending")
|
|
detailed.next_to(infinitely, RIGHT)
|
|
extending.next_to(infinitely, RIGHT)
|
|
Mobject(extending, infinitely, detailed).center()
|
|
arrows = Mobject(*[
|
|
Arrow(2*p, 4*p)
|
|
for theta in np.arange(np.pi/6, 2*np.pi, np.pi/3)
|
|
for p in [rotate_vector(RIGHT, theta)]
|
|
])
|
|
|
|
self.add(grid)
|
|
self.wait()
|
|
self.play(Transform(grid, space_mobject, run_time = 5))
|
|
self.remove(grid)
|
|
self.set_color_region(space_region, DARK_GREY)
|
|
self.wait()
|
|
self.add(infinitely, detailed)
|
|
self.wait()
|
|
self.play(DelayByOrder(Transform(detailed, extending)))
|
|
self.play(ShowCreation(arrows))
|
|
self.wait()
|
|
self.clear()
|
|
self.set_color_region(space_region, DARK_GREY)
|
|
self.play(ShowCreation(line))
|
|
self.play(Transform(line, curve, run_time = 5))
|
|
|
|
|
|
|
|
class HistoryOfDiscover(Scene):
|
|
def construct(self):
|
|
time_line = get_time_line()
|
|
time_line.shift(-time_line.number_to_point(1900))
|
|
hilbert_curve = HilbertCurve(order = 3)
|
|
peano_curve = PeanoCurve(order = 2)
|
|
for curve in hilbert_curve, peano_curve:
|
|
curve.scale(0.5)
|
|
hilbert_curve.to_corner(DOWN+RIGHT)
|
|
peano_curve.to_corner(UP+LEFT)
|
|
squares = Mobject(*[
|
|
Square(side_length=3, color=WHITE).replace(curve)
|
|
for curve in (hilbert_curve, peano_curve)
|
|
])
|
|
|
|
|
|
self.add(time_line)
|
|
self.wait()
|
|
for year, curve, vect, text in [
|
|
(1890, peano_curve, UP, "Peano Curve"),
|
|
(1891, hilbert_curve, DOWN, "Hilbert Curve"),
|
|
]:
|
|
point = time_line.number_to_point(year)
|
|
point[1] = 0.2
|
|
arrow = Arrow(point+2*vect, point, buff = 0.1)
|
|
arrow.set_color_by_gradient(curve.start_color, curve.end_color)
|
|
year_mob = TexMobject(str(year))
|
|
year_mob.next_to(arrow, vect)
|
|
words = TextMobject(text)
|
|
words.next_to(year_mob, vect)
|
|
|
|
self.play(
|
|
ShowCreation(arrow),
|
|
ShimmerIn(year_mob),
|
|
ShimmerIn(words)
|
|
)
|
|
self.play(ShowCreation(curve))
|
|
self.wait()
|
|
self.play(ShowCreation(squares))
|
|
self.wait()
|
|
self.play(ApplyMethod(
|
|
Mobject(*self.mobjects).shift, 20*(DOWN+RIGHT)
|
|
))
|
|
|
|
|
|
|
|
class DefinitionOfCurve(Scene):
|
|
def construct(self):
|
|
start_words = TextMobject([
|
|
"``", "Space Filling", "Curve ''",
|
|
]).to_edge(TOP, buff = 0.25)
|
|
quote, space_filling, curve_quote = start_words.copy().split()
|
|
curve_quote.shift(
|
|
space_filling.get_left()-\
|
|
curve_quote.get_left()
|
|
)
|
|
space_filling = Point(space_filling.get_center())
|
|
end_words = Mobject(*[
|
|
quote, space_filling, curve_quote
|
|
]).center().to_edge(TOP, buff = 0.25)
|
|
space_filling_fractal = TextMobject("""
|
|
``Space Filling Fractal''
|
|
""").to_edge(UP)
|
|
curve = HilbertCurve(order = 2).shift(DOWN)
|
|
fine_curve = HilbertCurve(order = 8)
|
|
fine_curve.replace(curve)
|
|
dots = Mobject(*[
|
|
Dot(
|
|
curve.points[n*curve.get_num_points()/15],
|
|
color = YELLOW_C
|
|
)
|
|
for n in range(1, 15)
|
|
if n not in [4, 11]
|
|
])
|
|
|
|
start_words.shift(2*(UP+LEFT))
|
|
self.play(
|
|
ApplyMethod(start_words.shift, 2*(DOWN+RIGHT))
|
|
)
|
|
self.wait()
|
|
self.play(Transform(start_words, end_words))
|
|
self.wait()
|
|
self.play(ShowCreation(curve))
|
|
self.wait()
|
|
self.play(ShowCreation(
|
|
dots,
|
|
run_time = 3,
|
|
))
|
|
self.wait()
|
|
self.clear()
|
|
self.play(ShowCreation(fine_curve, run_time = 5))
|
|
self.wait()
|
|
self.play(ShimmerIn(space_filling_fractal))
|
|
self.wait()
|
|
|
|
|
|
class PseudoHilbertCurvesDontFillSpace(Scene):
|
|
def construct(self):
|
|
curve = HilbertCurve(order = 1)
|
|
grid = Grid(2, 2, stroke_width=1)
|
|
self.add(grid, curve)
|
|
for order in range(2, 6):
|
|
self.wait()
|
|
new_grid = Grid(2**order, 2**order, stroke_width=1)
|
|
self.play(
|
|
ShowCreation(new_grid),
|
|
Animation(curve)
|
|
)
|
|
self.remove(grid)
|
|
grid = new_grid
|
|
self.play(Transform(
|
|
curve, HilbertCurve(order = order)
|
|
))
|
|
|
|
|
|
square = Square(side_length = 6, color = WHITE)
|
|
square.corner = Mobject1D()
|
|
square.corner.add_line(3*DOWN, ORIGIN)
|
|
square.corner.add_line(ORIGIN, 3*RIGHT)
|
|
square.digest_mobject_attrs()
|
|
square.scale(2**(-5))
|
|
square.corner.set_color(
|
|
Color(rgb = curve.rgbas[curve.get_num_points()/3])
|
|
)
|
|
square.shift(
|
|
grid.get_corner(UP+LEFT)-\
|
|
square.get_corner(UP+LEFT)
|
|
)
|
|
|
|
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(grid),
|
|
FadeOut(curve),
|
|
FadeIn(square)
|
|
)
|
|
self.play(
|
|
ApplyMethod(square.replace, grid)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class HilbertCurveIsLimit(Scene):
|
|
def construct(self):
|
|
mathy, bubble = get_mathy_and_bubble()
|
|
bubble.write(
|
|
"A Hilbert curve is the \\\\ limit of all these \\dots"
|
|
)
|
|
|
|
self.add(mathy, bubble)
|
|
self.play(ShimmerIn(bubble.content))
|
|
self.wait()
|
|
|
|
|
|
class DefiningCurves(Scene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
["One does not simply define the limit \\\\ \
|
|
of a sequence of","curves","\\dots"]
|
|
)
|
|
top_words = TextMobject([
|
|
"curves", "are functions"
|
|
]).to_edge(UP)
|
|
curves1 = words.split()[1]
|
|
curves2 = top_words.split()[0]
|
|
words.ingest_submobjects()
|
|
number = TexMobject("0.27")
|
|
pair = TexMobject("(0.53, 0.02)")
|
|
pair.next_to(number, buff = 2)
|
|
arrow = Arrow(number, pair)
|
|
Mobject(number, arrow, pair).center().shift(UP)
|
|
number_line = UnitInterval()
|
|
number_line.stretch_to_fit_width(5)
|
|
number_line.to_edge(LEFT).shift(DOWN)
|
|
grid = Grid(4, 4).scale(0.4)
|
|
grid.next_to(number_line, buff = 2)
|
|
low_arrow = Arrow(number_line, grid)
|
|
|
|
self.play(ShimmerIn(words))
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(words),
|
|
ApplyMethod(curves1.replace, curves2),
|
|
ShimmerIn(top_words.split()[1])
|
|
)
|
|
self.wait()
|
|
self.play(FadeIn(number))
|
|
self.play(ShowCreation(arrow))
|
|
self.play(FadeIn(pair))
|
|
self.wait()
|
|
self.play(ShowCreation(number_line))
|
|
self.play(ShowCreation(low_arrow))
|
|
self.play(ShowCreation(grid))
|
|
self.wait()
|
|
|
|
|
|
class PseudoHilbertCurveAsFunctionExample(Scene):
|
|
args_list = [(2,), (3,)]
|
|
|
|
# For subclasses to turn args in the above
|
|
# list into stings which can be appended to the name
|
|
@staticmethod
|
|
def args_to_string(order):
|
|
return "Order%d"%order
|
|
|
|
@staticmethod
|
|
def string_to_args(order_str):
|
|
return int(order_str)
|
|
|
|
|
|
def construct(self, order):
|
|
if order == 2:
|
|
result_tex = "(0.125, 0.75)"
|
|
elif order == 3:
|
|
result_tex = "(0.0758, 0.6875)"
|
|
|
|
phc, arg, result = TexMobject([
|
|
"\\text{PHC}_%d"%order,
|
|
"(0.3)",
|
|
"= %s"%result_tex
|
|
]).to_edge(UP).split()
|
|
function = TextMobject("Function", size = "\\normal")
|
|
function.shift(phc.get_center()+DOWN+2*LEFT)
|
|
function_arrow = Arrow(function, phc)
|
|
|
|
line = Line(5*LEFT, 5*RIGHT)
|
|
curve = HilbertCurve(order = order)
|
|
line.match_colors(curve)
|
|
grid = Grid(2**order, 2**order)
|
|
grid.fade()
|
|
for mob in curve, grid:
|
|
mob.scale(0.7)
|
|
index = int(0.3*line.get_num_points())
|
|
dot1 = Dot(line.points[index])
|
|
arrow1 = Arrow(arg, dot1, buff = 0.1)
|
|
dot2 = Dot(curve.points[index])
|
|
arrow2 = Arrow(result.get_bottom(), dot2, buff = 0.1)
|
|
|
|
self.add(phc)
|
|
self.play(
|
|
ShimmerIn(function),
|
|
ShowCreation(function_arrow)
|
|
)
|
|
self.wait()
|
|
self.remove(function_arrow, function)
|
|
self.play(ShowCreation(line))
|
|
self.wait()
|
|
self.play(
|
|
ShimmerIn(arg),
|
|
ShowCreation(arrow1),
|
|
ShowCreation(dot1)
|
|
)
|
|
self.wait()
|
|
self.remove(arrow1)
|
|
self.play(
|
|
FadeIn(grid),
|
|
Transform(line, curve),
|
|
Transform(dot1, dot2),
|
|
run_time = 2
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
ShimmerIn(result),
|
|
ShowCreation(arrow2)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
|
|
class ContinuityRequired(Scene):
|
|
def construct(self):
|
|
words = TextMobject([
|
|
"A function must be",
|
|
"\\emph{continuous}",
|
|
"if it is to represent a curve."
|
|
])
|
|
words.split()[1].set_color(YELLOW_C)
|
|
self.add(words)
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
class FormalDefinitionOfContinuity(Scene):
|
|
def construct(self):
|
|
self.setup()
|
|
self.label_spaces()
|
|
self.move_dot()
|
|
self.label_jump()
|
|
self.draw_circles()
|
|
self.vary_circle_sizes()
|
|
self.discontinuous_point()
|
|
|
|
|
|
def setup(self):
|
|
self.input_color = YELLOW_C
|
|
self.output_color = RED
|
|
def spiril(t):
|
|
theta = 2*np.pi*t
|
|
return t*np.cos(theta)*RIGHT+t*np.sin(theta)*UP
|
|
|
|
self.spiril1 = ParametricFunction(
|
|
lambda t : 1.5*RIGHT + DOWN + 2*spiril(t),
|
|
density = 5*DEFAULT_POINT_DENSITY_1D,
|
|
)
|
|
self.spiril2 = ParametricFunction(
|
|
lambda t : 5.5*RIGHT + UP - 2*spiril(1-t),
|
|
density = 5*DEFAULT_POINT_DENSITY_1D,
|
|
)
|
|
Mobject.align_data(self.spiril1, self.spiril2)
|
|
self.output = Mobject(self.spiril1, self.spiril2)
|
|
self.output.ingest_submobjects()
|
|
self.output.set_color(GREEN_A)
|
|
|
|
self.interval = UnitInterval()
|
|
self.interval.set_width(FRAME_X_RADIUS-1)
|
|
self.interval.to_edge(LEFT)
|
|
|
|
self.input_dot = Dot(color = self.input_color)
|
|
self.output_dot = self.input_dot.copy().set_color(self.output_color)
|
|
left, right = self.interval.get_left(), self.interval.get_right()
|
|
self.input_homotopy = lambda x_y_z_t : (x_y_z_t[0], x_y_z_t[1], x_y_z_t[3]) + interpolate(left, right, x_y_z_t[3])
|
|
output_size = self.output.get_num_points()-1
|
|
output_points = self.output.points
|
|
self.output_homotopy = lambda x_y_z_t1 : (x_y_z_t1[0], x_y_z_t1[1], x_y_z_t1[2]) + output_points[int(x_y_z_t1[3]*output_size)]
|
|
|
|
def get_circles_and_points(self, min_input, max_input):
|
|
input_left, input_right = [
|
|
self.interval.number_to_point(num)
|
|
for num in (min_input, max_input)
|
|
]
|
|
input_circle = Circle(
|
|
radius = get_norm(input_left-input_right)/2,
|
|
color = WHITE
|
|
)
|
|
input_circle.shift((input_left+input_right)/2)
|
|
|
|
input_points = Line(
|
|
input_left, input_right,
|
|
color = self.input_color
|
|
)
|
|
output_points = Mobject(color = self.output_color)
|
|
n = self.output.get_num_points()
|
|
output_points.add_points(
|
|
self.output.points[int(min_input*n):int(max_input*n)]
|
|
)
|
|
output_center = output_points.points[int(0.5*output_points.get_num_points())]
|
|
max_distance = get_norm(output_center-output_points.points[-1])
|
|
output_circle = Circle(
|
|
radius = max_distance,
|
|
color = WHITE
|
|
)
|
|
output_circle.shift(output_center)
|
|
return (
|
|
input_circle,
|
|
input_points,
|
|
output_circle,
|
|
output_points
|
|
)
|
|
|
|
|
|
def label_spaces(self):
|
|
input_space = TextMobject("Input Space")
|
|
input_space.to_edge(UP)
|
|
input_space.shift(LEFT*FRAME_X_RADIUS/2)
|
|
output_space = TextMobject("Output Space")
|
|
output_space.to_edge(UP)
|
|
output_space.shift(RIGHT*FRAME_X_RADIUS/2)
|
|
line = Line(
|
|
UP*FRAME_Y_RADIUS, DOWN*FRAME_Y_RADIUS,
|
|
color = WHITE
|
|
)
|
|
self.play(
|
|
ShimmerIn(input_space),
|
|
ShimmerIn(output_space),
|
|
ShowCreation(line),
|
|
ShowCreation(self.interval),
|
|
)
|
|
self.wait()
|
|
|
|
def move_dot(self):
|
|
kwargs = {
|
|
"rate_func" : None,
|
|
"run_time" : 3
|
|
}
|
|
self.play(
|
|
Homotopy(self.input_homotopy, self.input_dot, **kwargs),
|
|
Homotopy(self.output_homotopy, self.output_dot, **kwargs),
|
|
ShowCreation(self.output, **kwargs)
|
|
)
|
|
self.wait()
|
|
|
|
def label_jump(self):
|
|
jump_points = Mobject(
|
|
Point(self.spiril1.points[-1]),
|
|
Point(self.spiril2.points[0])
|
|
)
|
|
self.brace = Brace(jump_points, RIGHT)
|
|
self.jump = TextMobject("Jump")
|
|
self.jump.next_to(self.brace, RIGHT)
|
|
self.play(
|
|
GrowFromCenter(self.brace),
|
|
ShimmerIn(self.jump)
|
|
)
|
|
self.wait()
|
|
self.remove(self.brace, self.jump)
|
|
|
|
|
|
def draw_circles(self):
|
|
input_value = 0.45
|
|
input_radius = 0.04
|
|
for dot in self.input_dot, self.output_dot:
|
|
dot.center()
|
|
kwargs = {
|
|
"rate_func" : lambda t : interpolate(1, input_value, smooth(t))
|
|
}
|
|
self.play(
|
|
Homotopy(self.input_homotopy, self.input_dot, **kwargs),
|
|
Homotopy(self.output_homotopy, self.output_dot, **kwargs)
|
|
)
|
|
|
|
A, B = list(map(Mobject.get_center, [self.input_dot, self.output_dot]))
|
|
A_text = TextMobject("A")
|
|
A_text.shift(A+2*(LEFT+UP))
|
|
A_arrow = Arrow(
|
|
A_text, self.input_dot,
|
|
color = self.input_color
|
|
)
|
|
B_text = TextMobject("B")
|
|
B_text.shift(B+2*RIGHT+DOWN)
|
|
B_arrow = Arrow(
|
|
B_text, self.output_dot,
|
|
color = self.output_color
|
|
)
|
|
tup = self.get_circles_and_points(
|
|
input_value-input_radius,
|
|
input_value+input_radius
|
|
)
|
|
input_circle, input_points, output_circle, output_points = tup
|
|
|
|
for text, arrow in [(A_text, A_arrow), (B_text, B_arrow)]:
|
|
self.play(
|
|
ShimmerIn(text),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait()
|
|
self.remove(A_text, A_arrow, B_text, B_arrow)
|
|
self.play(ShowCreation(input_circle))
|
|
self.wait()
|
|
self.play(ShowCreation(input_points))
|
|
self.wait()
|
|
input_points_copy = input_points.copy()
|
|
self.play(
|
|
Transform(input_points_copy, output_points),
|
|
run_time = 2
|
|
)
|
|
self.wait()
|
|
self.play(ShowCreation(output_circle))
|
|
self.wait()
|
|
self.wait()
|
|
self.remove(*[
|
|
input_circle, input_points,
|
|
output_circle, input_points_copy
|
|
])
|
|
|
|
|
|
def vary_circle_sizes(self):
|
|
input_value = 0.45
|
|
radius = 0.04
|
|
vary_circles = VaryCircles(
|
|
self, input_value, radius,
|
|
run_time = 5,
|
|
)
|
|
self.play(vary_circles)
|
|
self.wait()
|
|
text = TextMobject("Function is ``Continuous at A''")
|
|
text.shift(2*UP).to_edge(LEFT)
|
|
arrow = Arrow(text, self.input_dot)
|
|
self.play(
|
|
ShimmerIn(text),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait()
|
|
self.remove(vary_circles.mobject, text, arrow)
|
|
|
|
def discontinuous_point(self):
|
|
point_description = TextMobject(
|
|
"Point where the function jumps"
|
|
)
|
|
point_description.shift(3*RIGHT)
|
|
discontinuous_at_A = TextMobject(
|
|
"``Discontinuous at A''",
|
|
size = "\\Large"
|
|
)
|
|
discontinuous_at_A.shift(2*UP).to_edge(LEFT)
|
|
text = TextMobject("""
|
|
Circle around ouput \\\\
|
|
points can never \\\\
|
|
be smaller than \\\\
|
|
the jump
|
|
""")
|
|
text.scale(0.75)
|
|
text.shift(3.5*RIGHT)
|
|
|
|
input_value = 0.5
|
|
input_radius = 0.04
|
|
vary_circles = VaryCircles(
|
|
self, input_value, input_radius,
|
|
run_time = 5,
|
|
)
|
|
for dot in self.input_dot, self.output_dot:
|
|
dot.center()
|
|
kwargs = {
|
|
"rate_func" : lambda t : interpolate(0.45, input_value, smooth(t))
|
|
}
|
|
self.play(
|
|
Homotopy(self.input_homotopy, self.input_dot, **kwargs),
|
|
Homotopy(self.output_homotopy, self.output_dot, **kwargs)
|
|
)
|
|
discontinuous_arrow = Arrow(discontinuous_at_A, self.input_dot)
|
|
arrow = Arrow(
|
|
point_description, self.output_dot,
|
|
buff = 0.05,
|
|
color = self.output_color
|
|
)
|
|
self.play(
|
|
ShimmerIn(point_description),
|
|
ShowCreation(arrow)
|
|
)
|
|
self.wait()
|
|
self.remove(point_description, arrow)
|
|
|
|
tup = self.get_circles_and_points(
|
|
input_value-input_radius,
|
|
input_value+input_radius
|
|
)
|
|
input_circle, input_points, output_circle, output_points = tup
|
|
input_points_copy = input_points.copy()
|
|
self.play(ShowCreation(input_circle))
|
|
self.play(ShowCreation(input_points))
|
|
self.play(
|
|
Transform(input_points_copy, output_points),
|
|
run_time = 2
|
|
)
|
|
self.play(ShowCreation(output_circle))
|
|
self.wait()
|
|
self.play(ShimmerIn(text))
|
|
self.remove(input_circle, input_points, output_circle, input_points_copy)
|
|
self.play(vary_circles)
|
|
self.wait()
|
|
self.play(
|
|
ShimmerIn(discontinuous_at_A),
|
|
ShowCreation(discontinuous_arrow)
|
|
)
|
|
self.wait(3)
|
|
self.remove(vary_circles.mobject, discontinuous_at_A, discontinuous_arrow)
|
|
|
|
def continuous_point(self):
|
|
pass
|
|
|
|
|
|
|
|
class VaryCircles(Animation):
|
|
def __init__(self, scene, input_value, radius, **kwargs):
|
|
digest_locals(self)
|
|
Animation.__init__(self, Mobject(), **kwargs)
|
|
|
|
def interpolate_mobject(self, alpha):
|
|
radius = self.radius + 0.9*self.radius*np.sin(1.5*np.pi*alpha)
|
|
self.mobject = Mobject(*self.scene.get_circles_and_points(
|
|
self.input_value-radius,
|
|
self.input_value+radius
|
|
)).ingest_submobjects()
|
|
|
|
|
|
class FunctionIsContinuousText(Scene):
|
|
def construct(self):
|
|
all_points = TextMobject("$f$ is continuous at every input point")
|
|
continuous = TextMobject("$f$ is continuous")
|
|
all_points.shift(UP)
|
|
continuous.shift(DOWN)
|
|
arrow = Arrow(all_points, continuous)
|
|
|
|
self.play(ShimmerIn(all_points))
|
|
self.play(ShowCreation(arrow))
|
|
self.play(ShimmerIn(continuous))
|
|
self.wait()
|
|
|
|
|
|
class DefineActualHilbertCurveText(Scene):
|
|
def construct(self):
|
|
self.add(TextMobject("""
|
|
Finally define a Hilbert Curve\\dots
|
|
"""))
|
|
self.wait()
|
|
|
|
|
|
class ReliesOnWonderfulProperty(Scene):
|
|
def construct(self):
|
|
self.add(TextMobject("""
|
|
\\dots which relies on a certain property
|
|
of Pseudo-Hilbert-curves.
|
|
"""))
|
|
self.wait()
|
|
|
|
|
|
class WonderfulPropertyOfPseudoHilbertCurves(Scene):
|
|
def construct(self):
|
|
val = 0.3
|
|
text = TextMobject([
|
|
"PHC", "$_n", "(", "%3.1f"%val, ")$",
|
|
" has a ", "limit point ", "as $n \\to \\infty$"
|
|
])
|
|
func_parts = text.copy().split()[:5]
|
|
Mobject(*func_parts).center().to_edge(UP)
|
|
num_str, val_str = func_parts[1], func_parts[3]
|
|
curve = UnitInterval()
|
|
curve.sort_points(lambda p : p[0])
|
|
dot = Dot().shift(curve.number_to_point(val))
|
|
arrow = Arrow(val_str, dot, buff = 0.1)
|
|
curve.add_numbers(0, 1)
|
|
|
|
self.play(ShowCreation(curve))
|
|
self.play(
|
|
ShimmerIn(val_str),
|
|
ShowCreation(arrow),
|
|
ShowCreation(dot)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(arrow),
|
|
*[
|
|
FadeIn(func_parts[i])
|
|
for i in (0, 1, 2, 4)
|
|
]
|
|
)
|
|
for num in range(2,9):
|
|
new_curve = HilbertCurve(order = num)
|
|
new_curve.scale(0.8)
|
|
new_dot = Dot(new_curve.points[int(val*new_curve.get_num_points())])
|
|
new_num_str = TexMobject(str(num)).replace(num_str)
|
|
self.play(
|
|
Transform(curve, new_curve),
|
|
Transform(dot, new_dot),
|
|
Transform(num_str, new_num_str)
|
|
)
|
|
self.wait()
|
|
|
|
text.to_edge(UP)
|
|
text_parts = text.split()
|
|
for index in 1, -1:
|
|
text_parts[index].set_color()
|
|
starters = Mobject(*func_parts + [
|
|
Point(mob.get_center(), stroke_width=1)
|
|
for mob in text_parts[5:]
|
|
])
|
|
self.play(Transform(starters, text))
|
|
arrow = Arrow(text_parts[-2].get_bottom(), dot, buff = 0.1)
|
|
self.play(ShowCreation(arrow))
|
|
self.wait()
|
|
|
|
class FollowManyPoints(Scene):
|
|
def construct(self):
|
|
text = TextMobject([
|
|
"PHC", "_n", "(", "x", ")$",
|
|
" has a limit point ", "as $n \\to \\infty$",
|
|
"\\\\ for all $x$"
|
|
])
|
|
parts = text.split()
|
|
parts[-1].next_to(Mobject(*parts[:-1]), DOWN)
|
|
parts[-1].set_color(BLUE)
|
|
parts[3].set_color(BLUE)
|
|
parts[1].set_color()
|
|
parts[-2].set_color()
|
|
text.to_edge(UP)
|
|
curve = UnitInterval()
|
|
curve.sort_points(lambda p : p[0])
|
|
vals = np.arange(0.1, 1, 0.1)
|
|
dots = Mobject(*[
|
|
Dot(curve.number_to_point(val))
|
|
for val in vals
|
|
])
|
|
curve.add_numbers(0, 1)
|
|
starter_dots = dots.copy().ingest_submobjects()
|
|
starter_dots.shift(2*UP)
|
|
|
|
self.add(curve, text)
|
|
self.wait()
|
|
self.play(DelayByOrder(ApplyMethod(starter_dots.shift, 2*DOWN)))
|
|
self.wait()
|
|
self.remove(starter_dots)
|
|
self.add(dots)
|
|
for num in range(1, 10):
|
|
new_curve = HilbertCurve(order = num)
|
|
new_curve.scale(0.8)
|
|
new_dots = Mobject(*[
|
|
Dot(new_curve.points[int(val*new_curve.get_num_points())])
|
|
for val in vals
|
|
])
|
|
self.play(
|
|
Transform(curve, new_curve),
|
|
Transform(dots, new_dots),
|
|
)
|
|
# self.wait()
|
|
|
|
|
|
class FormalDefinitionOfHilbertCurve(Scene):
|
|
def construct(self):
|
|
val = 0.7
|
|
text = TexMobject([
|
|
"\\text{HC}(", "x", ")",
|
|
"=\\lim_{n \\to \\infty}\\text{PHC}_n(", "x", ")"
|
|
])
|
|
text.to_edge(UP)
|
|
x1 = text.split()[1]
|
|
x2 = text.split()[-2]
|
|
x2.set_color(BLUE)
|
|
explanation = TextMobject("Actual Hilbert curve function")
|
|
exp_arrow = Arrow(explanation, text.split()[0])
|
|
curve = UnitInterval()
|
|
dot = Dot(curve.number_to_point(val))
|
|
x_arrow = Arrow(x1.get_bottom(), dot, buff = 0)
|
|
curve.sort_points(lambda p : p[0])
|
|
curve.add_numbers(0, 1)
|
|
|
|
self.add(*text.split()[:3])
|
|
self.play(
|
|
ShimmerIn(explanation),
|
|
ShowCreation(exp_arrow)
|
|
)
|
|
self.wait()
|
|
self.remove(explanation, exp_arrow)
|
|
self.play(ShowCreation(curve))
|
|
self.play(
|
|
ApplyMethod(x1.set_color, BLUE),
|
|
ShowCreation(x_arrow),
|
|
ShowCreation(dot)
|
|
)
|
|
self.wait()
|
|
self.remove(x_arrow)
|
|
limit = Mobject(*text.split()[3:]).ingest_submobjects()
|
|
limit.stroke_width = 1
|
|
self.play(ShimmerIn(limit))
|
|
for num in range(1, 9):
|
|
new_curve = HilbertCurve(order = num)
|
|
new_curve.scale(0.8)
|
|
new_dot = Dot(new_curve.points[int(val*new_curve.get_num_points())])
|
|
self.play(
|
|
Transform(curve, new_curve),
|
|
Transform(dot, new_dot),
|
|
)
|
|
|
|
|
|
class CouldNotDefineForSnakeCurve(Scene):
|
|
def construct(self):
|
|
self.add(TextMobject("""
|
|
You could not define a limit curve from
|
|
snake curves.
|
|
"""))
|
|
self.wait()
|
|
|
|
class ThreeThingsToProve(Scene):
|
|
def construct(self):
|
|
definition = TexMobject([
|
|
"\\text{HC}(", "x", ")",
|
|
"=\\lim_{n \\to \\infty}\\text{PHC}_n(", "x", ")"
|
|
])
|
|
definition.to_edge(UP)
|
|
definition.split()[1].set_color(BLUE)
|
|
definition.split()[-2].set_color(BLUE)
|
|
intro = TextMobject("Three things need to be proven")
|
|
prove_that = TextMobject("Prove that HC is $\\dots$")
|
|
prove_that.scale(0.7)
|
|
prove_that.to_edge(LEFT)
|
|
items = TextMobject([
|
|
"\\begin{enumerate}",
|
|
"\\item Well-defined: ",
|
|
"Points on Pseudo-Hilbert-curves really do converge",
|
|
"\\item A Curve: ",
|
|
"HC is continuous",
|
|
"\\item Space-filling: ",
|
|
"Each point in the unit square is an output of HC",
|
|
"\\end{enumerate}",
|
|
]).split()
|
|
items[1].set_color(GREEN)
|
|
items[3].set_color(YELLOW_C)
|
|
items[5].set_color(MAROON)
|
|
Mobject(*items).to_edge(RIGHT)
|
|
|
|
self.add(definition)
|
|
self.play(ShimmerIn(intro))
|
|
self.wait()
|
|
self.play(Transform(intro, prove_that))
|
|
for item in items[1:-1]:
|
|
self.play(ShimmerIn(item))
|
|
self.wait()
|
|
|
|
|
|
|
|
class TilingSpace(Scene):
|
|
def construct(self):
|
|
coords_set = [ORIGIN]
|
|
for n in range(int(FRAME_WIDTH)):
|
|
for vect in UP, RIGHT:
|
|
for k in range(n):
|
|
new_coords = coords_set[-1]+((-1)**n)*vect
|
|
coords_set.append(new_coords)
|
|
square = Square(side_length = 1, color = WHITE)
|
|
squares = Mobject(*[
|
|
square.copy().shift(coords)
|
|
for coords in coords_set
|
|
]).ingest_submobjects()
|
|
self.play(
|
|
DelayByOrder(FadeIn(squares)),
|
|
run_time = 3
|
|
)
|
|
curve = HilbertCurve(order = 6).scale(1./6)
|
|
all_curves = Mobject(*[
|
|
curve.copy().shift(coords)
|
|
for coords in coords_set
|
|
]).ingest_submobjects()
|
|
all_curves.thin_out(10)
|
|
self.play(ShowCreation(
|
|
all_curves,
|
|
rate_func=linear,
|
|
run_time = 15
|
|
))
|
|
|
|
|
|
class ColorIntervals(Scene):
|
|
def construct(self):
|
|
number_line = NumberLine(
|
|
numerical_radius = 5,
|
|
number_at_center = 5,
|
|
leftmost_tick = 0,
|
|
density = 2*DEFAULT_POINT_DENSITY_1D
|
|
)
|
|
number_line.shift(2*RIGHT)
|
|
number_line.add_numbers()
|
|
number_line.scale(2)
|
|
brace = Brace(Mobject(
|
|
*number_line.submobjects[:2]
|
|
))
|
|
|
|
self.add(number_line)
|
|
for n in range(0, 10, 2):
|
|
if n == 0:
|
|
brace_anim = GrowFromCenter(brace)
|
|
else:
|
|
brace_anim = ApplyMethod(brace.shift, 2*RIGHT)
|
|
self.play(
|
|
ApplyMethod(
|
|
number_line.set_color,
|
|
RED,
|
|
lambda p : p[0] > n-6.2 and p[0] < n-4 and p[1] > -0.4
|
|
),
|
|
brace_anim
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|