Files
manim/old_projects/fractal_dimension.py
2019-05-02 20:36:14 -07:00

2932 lines
87 KiB
Python

from manimlib.imports import *
from functools import reduce
def break_up(mobject, factor = 1.3):
mobject.scale_in_place(factor)
for submob in mobject:
submob.scale_in_place(1./factor)
return mobject
class Britain(SVGMobject):
CONFIG = {
"file_name" : "Britain.svg",
"stroke_width" : 0,
"fill_color" : BLUE_D,
"fill_opacity" : 1,
"height" : 5,
"mark_paths_closed" : True,
}
def __init__(self, **kwargs):
SVGMobject.__init__(self, **kwargs)
self.points = self[0].points
self.submobjects = []
self.set_height(self.height)
self.center()
class Norway(Britain):
CONFIG = {
"file_name" : "Norway",
"mark_paths_closed" : False
}
class KochTest(Scene):
def construct(self):
koch = KochCurve(order = 5, stroke_width = 2)
self.play(ShowCreation(koch, run_time = 3))
self.play(
koch.scale, 3, koch.get_left(),
koch.set_stroke, None, 4
)
self.wait()
class SierpinskiTest(Scene):
def construct(self):
sierp = Sierpinski(
order = 5,
)
self.play(FadeIn(
sierp,
run_time = 5,
lag_ratio = 0.5,
))
self.wait()
# self.play(sierp.scale, 2, sierp.get_top())
# self.wait(3)
###################################
class ZoomInOnFractal(PiCreatureScene):
CONFIG = {
"fractal_order" : 6,
"num_zooms" : 5,
"fractal_class" : DiamondFractal,
"index_to_replace" : 0,
}
def construct(self):
morty = self.pi_creature
fractal = self.fractal_class(order = self.fractal_order)
fractal.show()
fractal = self.introduce_fractal()
self.change_mode("thinking")
self.blink()
self.zoom_in(fractal)
def introduce_fractal(self):
fractal = self.fractal_class(order = 0)
self.play(FadeIn(fractal))
for order in range(1, self.fractal_order+1):
new_fractal = self.fractal_class(order = order)
self.play(
Transform(fractal, new_fractal, run_time = 2),
self.pi_creature.change_mode, "hooray"
)
return fractal
def zoom_in(self, fractal):
grower = fractal[self.index_to_replace]
grower_target = fractal.copy()
for x in range(self.num_zooms):
self.tweak_fractal_subpart(grower_target)
grower_family = grower.family_members_with_points()
everything = VGroup(*[
submob
for submob in fractal.family_members_with_points()
if not submob.is_off_screen()
if submob not in grower_family
])
everything.generate_target()
everything.target.shift(
grower_target.get_center()-grower.get_center()
)
everything.target.scale(
grower_target.get_height()/grower.get_height()
)
self.play(
Transform(grower, grower_target),
MoveToTarget(everything),
self.pi_creature.change_mode, "thinking",
run_time = 2
)
self.wait()
grower_target = grower.copy()
grower = grower[self.index_to_replace]
def tweak_fractal_subpart(self, subpart):
subpart.rotate_in_place(np.pi/4)
class WhatAreFractals(TeacherStudentsScene):
def construct(self):
self.student_says(
"But what \\emph{is} a fractal?",
student_index = 2,
width = 6
)
self.change_student_modes("thinking", "pondering", None)
self.wait()
name = TextMobject("Benoit Mandelbrot")
name.to_corner(UP+LEFT)
# picture = Rectangle(height = 4, width = 3)
picture = ImageMobject("Mandelbrot")
picture.set_height(4)
picture.next_to(name, DOWN)
self.play(
Write(name, run_time = 2),
FadeIn(picture),
*[
ApplyMethod(pi.look_at, name)
for pi in self.get_pi_creatures()
]
)
self.wait(2)
question = TextMobject("Aren't they", "self-similar", "shapes?")
question.set_color_by_tex("self-similar", YELLOW)
self.student_says(question)
self.play(self.get_teacher().change_mode, "happy")
self.wait(2)
class IntroduceVonKochCurve(Scene):
CONFIG = {
"order" : 5,
"stroke_width" : 3,
}
def construct(self):
snowflake = self.get_snowflake()
name = TextMobject("Von Koch Snowflake")
name.to_edge(UP)
self.play(ShowCreation(snowflake, run_time = 3))
self.play(Write(name, run_time = 2))
curve = self.isolate_one_curve(snowflake)
self.wait()
self.zoom_in_on(curve)
self.zoom_in_on(curve)
self.zoom_in_on(curve)
def get_snowflake(self):
triangle = RegularPolygon(n = 3, start_angle = np.pi/2)
triangle.set_height(4)
curves = VGroup(*[
KochCurve(
order = self.order,
stroke_width = self.stroke_width
)
for x in range(3)
])
for index, curve in enumerate(curves):
width = curve.get_width()
curve.move_to(
(np.sqrt(3)/6)*width*UP, DOWN
)
curve.rotate(-index*2*np.pi/3)
curves.set_color_by_gradient(BLUE, WHITE, BLUE)
return curves
def isolate_one_curve(self, snowflake):
self.play(*[
ApplyMethod(curve.shift, curve.get_center()/2)
for curve in snowflake
])
self.wait()
self.play(
snowflake.scale, 2.1,
snowflake.next_to, UP, DOWN
)
self.remove(*snowflake[1:])
return snowflake[0]
def zoom_in_on(self, curve):
larger_curve = KochCurve(
order = self.order+1,
stroke_width = self.stroke_width
)
larger_curve.replace(curve)
larger_curve.scale(3, about_point = curve.get_corner(DOWN+LEFT))
larger_curve.set_color_by_gradient(
curve[0].get_color(),
curve[-1].get_color(),
)
self.play(Transform(curve, larger_curve, run_time = 2))
n_parts = len(curve.split())
sub_portion = VGroup(*curve[:n_parts/4])
self.play(
sub_portion.set_color, YELLOW,
rate_func = there_and_back
)
self.wait()
class IntroduceSierpinskiTriangle(PiCreatureScene):
CONFIG = {
"order" : 7,
}
def construct(self):
sierp = Sierpinski(order = self.order)
sierp.save_state()
self.play(FadeIn(
sierp,
run_time = 2,
lag_ratio = 0.5
))
self.wait()
self.play(
self.pi_creature.change_mode, "pondering",
*[
ApplyMethod(submob.shift, submob.get_center())
for submob in sierp
]
)
self.wait()
for submob in sierp:
self.play(sierp.shift, -submob.get_center())
self.wait()
self.play(sierp.restore)
self.change_mode("happy")
self.wait()
class SelfSimilarFractalsAsSubset(Scene):
CONFIG = {
"fractal_width" : 1.5
}
def construct(self):
self.add_self_similar_fractals()
self.add_general_fractals()
def add_self_similar_fractals(self):
fractals = VGroup(
DiamondFractal(order = 5),
KochSnowFlake(order = 3),
Sierpinski(order = 5),
)
for submob in fractals:
submob.set_width(self.fractal_width)
fractals.arrange(RIGHT)
fractals[-1].next_to(VGroup(*fractals[:-1]), DOWN)
title = TextMobject("Self-similar fractals")
title.next_to(fractals, UP)
small_rect = Rectangle()
small_rect.replace(VGroup(fractals, title), stretch = True)
small_rect.scale_in_place(1.2)
self.small_rect = small_rect
group = VGroup(fractals, title, small_rect)
group.to_corner(UP+LEFT, buff = MED_LARGE_BUFF)
self.play(
Write(title),
ShowCreation(fractals),
run_time = 3
)
self.play(ShowCreation(small_rect))
self.wait()
def add_general_fractals(self):
big_rectangle = Rectangle(
width = FRAME_WIDTH - MED_LARGE_BUFF,
height = FRAME_HEIGHT - MED_LARGE_BUFF,
)
title = TextMobject("Fractals")
title.scale(1.5)
title.next_to(ORIGIN, RIGHT, buff = LARGE_BUFF)
title.to_edge(UP, buff = MED_LARGE_BUFF)
britain = Britain(
fill_opacity = 0,
stroke_width = 2,
stroke_color = WHITE,
)
britain.next_to(self.small_rect, RIGHT)
britain.shift(2*DOWN)
randy = Randolph().flip().scale(1.4)
randy.next_to(britain, buff = SMALL_BUFF)
randy.generate_target()
randy.target.change_mode("pleading")
fractalify(randy.target, order = 2)
self.play(
ShowCreation(big_rectangle),
Write(title),
)
self.play(ShowCreation(britain), run_time = 5)
self.play(
britain.set_fill, BLUE, 1,
britain.set_stroke, None, 0,
run_time = 2
)
self.play(FadeIn(randy))
self.play(MoveToTarget(randy, run_time = 2))
self.wait(2)
class ConstrastSmoothAndFractal(Scene):
CONFIG = {
"britain_zoom_point_proportion" : 0.45,
"scale_factor" : 50,
"fractalification_order" : 2,
"fractal_dimension" : 1.21,
}
def construct(self):
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
smooth = TextMobject("Smooth")
smooth.shift(FRAME_X_RADIUS*LEFT/2)
fractal = TextMobject("Fractal")
fractal.shift(FRAME_X_RADIUS*RIGHT/2)
VGroup(smooth, fractal).to_edge(UP)
background_rectangle = Rectangle(
height = FRAME_HEIGHT,
width = FRAME_X_RADIUS,
)
background_rectangle.to_edge(RIGHT, buff = 0)
background_rectangle.set_fill(BLACK, 1)
background_rectangle.set_stroke(width = 0)
self.add(v_line, background_rectangle, smooth, fractal)
britain = Britain(
fill_opacity = 0,
stroke_width = 2,
stroke_color = WHITE,
)[0]
anchors = britain.get_anchors()
smooth_britain = VMobject()
smooth_britain.set_points_smoothly(anchors[::10])
smooth_britain.center().shift(FRAME_X_RADIUS*LEFT/2)
index = np.argmax(smooth_britain.get_anchors()[:,0])
smooth_britain.zoom_point = smooth_britain.point_from_proportion(
self.britain_zoom_point_proportion
)
britain.shift(FRAME_X_RADIUS*RIGHT/2)
britain.zoom_point = britain.point_from_proportion(
self.britain_zoom_point_proportion
)
fractalify(
britain,
order = self.fractalification_order,
dimension = self.fractal_dimension,
)
britains = VGroup(britain, smooth_britain)
self.play(*[
ShowCreation(mob, run_time = 3)
for mob in britains
])
self.play(
britains.set_fill, BLUE, 1,
britains.set_stroke, None, 0,
)
self.wait()
self.play(
ApplyMethod(
smooth_britain.scale,
self.scale_factor,
smooth_britain.zoom_point
),
Animation(v_line),
Animation(background_rectangle),
ApplyMethod(
britain.scale,
self.scale_factor,
britain.zoom_point
),
Animation(smooth),
Animation(fractal),
run_time = 10,
)
self.wait(2)
class InfiniteKochZoom(Scene):
CONFIG = {
"order" : 6,
"left_point" : 3*LEFT,
}
def construct(self):
small_curve = self.get_curve(self.order)
larger_curve = self.get_curve(self.order + 1)
larger_curve.scale(3, about_point = small_curve.points[0])
self.play(Transform(small_curve, larger_curve, run_time = 2))
self.repeat_frames(5)
def get_curve(self, order):
koch_curve = KochCurve(
monochromatic = True,
order = order,
color = BLUE,
stroke_width = 2,
)
koch_curve.set_width(18)
koch_curve.shift(
self.left_point - koch_curve.points[0]
)
return koch_curve
class ShowIdealizations(Scene):
def construct(self):
arrow = DoubleArrow(FRAME_X_RADIUS*LEFT, FRAME_X_RADIUS*RIGHT)
arrow.shift(DOWN)
left_words = TextMobject("Idealization \\\\ as smooth")
middle_words = TextMobject("Nature")
right_words = TextMobject("""
Idealization
as perfectly
self-similar
""")
for words in left_words, middle_words, right_words:
words.scale(0.8)
words.next_to(arrow, DOWN)
left_words.to_edge(LEFT)
right_words.to_edge(RIGHT)
self.add(arrow, left_words, middle_words, right_words)
britain = Britain()[0]
britain.set_height(4)
britain.next_to(arrow, UP)
anchors = britain.get_anchors()
smooth_britain = VMobject()
smooth_britain.set_points_smoothly(anchors[::10])
smooth_britain.set_stroke(width = 0)
smooth_britain.set_fill(BLUE_D, opacity = 1)
smooth_britain.next_to(arrow, UP)
smooth_britain.to_edge(LEFT)
koch_snowflake = KochSnowFlake(order = 5, monochromatic = True)
koch_snowflake.set_stroke(width = 0)
koch_snowflake.set_fill(BLUE_D, opacity = 1)
koch_snowflake.set_height(3)
koch_snowflake.rotate(2*np.pi/3)
koch_snowflake.next_to(arrow, UP)
koch_snowflake.to_edge(RIGHT)
VGroup(smooth_britain, britain, koch_snowflake).set_color_by_gradient(
BLUE_B, BLUE_D
)
self.play(FadeIn(britain))
self.wait()
self.play(Transform(britain.copy(), smooth_britain))
self.wait()
self.play(Transform(britain.copy(), koch_snowflake))
self.wait()
self.wait(2)
class SayFractalDimension(TeacherStudentsScene):
def construct(self):
self.teacher_says("Fractal dimension")
self.change_student_modes("confused", "hesitant", "pondering")
self.wait(3)
class ExamplesOfDimension(Scene):
def construct(self):
labels = VGroup(*[
TextMobject("%s-dimensional"%s)
for s in ("1.585", "1.262", "1.21")
])
fractals = VGroup(*[
Sierpinski(order = 7),
KochSnowFlake(order = 5),
Britain(stroke_width = 2, fill_opacity = 0)
])
for fractal, vect in zip(fractals, [LEFT, ORIGIN, RIGHT]):
fractal.to_edge(vect)
fractals[2].shift(0.5*UP)
fractals[1].shift(0.5*RIGHT)
for fractal, label, vect in zip(fractals, labels, [DOWN, UP, DOWN]):
label.next_to(fractal, vect)
label.shift_onto_screen()
self.play(
ShowCreation(fractal),
Write(label),
run_time = 3
)
self.wait()
self.wait()
class FractalDimensionIsNonsense(Scene):
def construct(self):
morty = Mortimer().shift(DOWN+3*RIGHT)
mathy = Mathematician().shift(DOWN+3*LEFT)
morty.make_eye_contact(mathy)
self.add(morty, mathy)
self.play(
PiCreatureSays(
mathy, "It's 1.585-dimensional!",
target_mode = "hooray"
),
morty.change_mode, "hesitant"
)
self.play(Blink(morty))
self.play(
PiCreatureSays(morty, "Nonsense!", target_mode = "angry"),
FadeOut(mathy.bubble),
FadeOut(mathy.bubble.content),
mathy.change_mode, "guilty"
)
self.play(Blink(mathy))
self.wait()
class DimensionForNaturalNumbers(Scene):
def construct(self):
labels = VGroup(*[
TextMobject("%d-dimensional"%d)
for d in (1, 2, 3)
])
for label, vect in zip(labels, [LEFT, ORIGIN, RIGHT]):
label.to_edge(vect)
labels.shift(2*DOWN)
line = Line(DOWN+LEFT, 3*UP+RIGHT, color = BLUE)
line.next_to(labels[0], UP)
self.play(
Write(labels[0]),
ShowCreation(line)
)
self.wait()
for label in labels[1:]:
self.play(Write(label))
self.wait()
class Show2DPlanein3D(Scene):
def construct(self):
pass
class ShowCubeIn3D(Scene):
def construct(self):
pass
class OfCourseItsMadeUp(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
Fractal dimension
\\emph{is} a made up concept...
""")
self.change_student_modes(*["hesitant"]*3)
self.wait(2)
self.teacher_says(
"""But it's useful!""",
target_mode = "hooray"
)
self.change_student_modes(*["happy"]*3)
self.wait(3)
class FourSelfSimilarShapes(Scene):
CONFIG = {
"shape_width" : 2,
"sierpinski_order" : 6,
}
def construct(self):
titles = self.get_titles()
shapes = self.get_shapes(titles)
self.introduce_shapes(titles, shapes)
self.show_self_similarity(shapes)
self.mention_measurements()
def get_titles(self):
titles = VGroup(*list(map(TextMobject, [
"Line", "Square", "Cube", "Sierpinski"
])))
for title, x in zip(titles, np.linspace(-0.75, 0.75, 4)):
title.shift(x*FRAME_X_RADIUS*RIGHT)
titles.to_edge(UP)
return titles
def get_shapes(self, titles):
line = VGroup(
Line(LEFT, ORIGIN),
Line(ORIGIN, RIGHT)
)
line.set_color(BLUE_C)
square = VGroup(*[
Square().next_to(ORIGIN, vect, buff = 0)
for vect in compass_directions(start_vect = DOWN+LEFT)
])
square.set_stroke(width = 0)
square.set_fill(BLUE, 0.7)
cube = TextMobject("TODO")
cube.set_fill(opacity = 0)
sierpinski = Sierpinski(order = self.sierpinski_order)
shapes = VGroup(line, square, cube, sierpinski)
for shape, title in zip(shapes, titles):
shape.set_width(self.shape_width)
shape.next_to(title, DOWN, buff = MED_SMALL_BUFF)
line.shift(DOWN)
return shapes
def introduce_shapes(self, titles, shapes):
line, square, cube, sierpinski = shapes
brace = Brace(VGroup(*shapes[:3]), DOWN)
brace_text = brace.get_text("Not fractals")
self.play(ShowCreation(line))
self.play(GrowFromCenter(square))
self.play(FadeIn(cube))
self.play(ShowCreation(sierpinski))
self.wait()
self.play(
GrowFromCenter(brace),
FadeIn(brace_text)
)
self.wait()
self.play(*list(map(FadeOut, [brace, brace_text])))
self.wait()
for title in titles:
self.play(Write(title, run_time = 1))
self.wait(2)
def show_self_similarity(self, shapes):
shapes_copy = shapes.copy()
self.shapes_copy = shapes_copy
line, square, cube, sierpinski = shapes_copy
self.play(line.shift, 3*DOWN)
self.play(ApplyFunction(break_up, line))
self.wait()
brace = Brace(line[0], DOWN)
brace_text = brace.get_text("1/2")
self.play(
GrowFromCenter(brace),
Write(brace_text)
)
brace.add(brace_text)
self.wait()
self.play(square.next_to, square, DOWN, LARGE_BUFF)
self.play(ApplyFunction(break_up, square))
subsquare = square[0]
subsquare.save_state()
self.play(subsquare.replace, shapes[1])
self.wait()
self.play(subsquare.restore)
self.play(brace.next_to, subsquare, DOWN)
self.wait()
self.wait(5)#Handle cube
self.play(sierpinski.next_to, sierpinski, DOWN, LARGE_BUFF)
self.play(ApplyFunction(break_up, sierpinski))
self.wait()
self.play(brace.next_to, sierpinski[0], DOWN)
self.wait(2)
self.play(FadeOut(brace))
def mention_measurements(self):
line, square, cube, sierpinski = self.shapes_copy
labels = list(map(TextMobject, [
"$1/2$ length",
"$1/4$ area",
"$1/8$ volume",
"You'll see...",
]))
for label, shape in zip(labels, self.shapes_copy):
label.next_to(shape, DOWN)
label.to_edge(DOWN, buff = MED_LARGE_BUFF)
if label is labels[-1]:
label.shift(0.1*UP) #Dumb
self.play(
Write(label, run_time = 1),
shape[0].set_color, YELLOW
)
self.wait()
class BreakUpCubeIn3D(Scene):
def construct(self):
pass
class BrokenUpCubeIn3D(Scene):
def construct(self):
pass
class GeneralWordForMeasurement(Scene):
def construct(self):
measure = TextMobject("``Measure''")
measure.to_edge(UP)
mass = TextMobject("Mass")
mass.move_to(measure)
words = VGroup(*list(map(TextMobject, [
"Length", "Area", "Volume"
])))
words.arrange(RIGHT, buff = 2*LARGE_BUFF)
words.next_to(measure, DOWN, buff = 2*LARGE_BUFF)
colors = color_gradient([BLUE_B, BLUE_D], len(words))
for word, color in zip(words, colors):
word.set_color(color)
lines = VGroup(*[
Line(
measure.get_bottom(), word.get_top(),
color = word.get_color(),
buff = MED_SMALL_BUFF
)
for word in words
])
for word in words:
self.play(FadeIn(word))
self.play(ShowCreation(lines, run_time = 2))
self.wait()
self.play(Write(measure))
self.wait(2)
self.play(Transform(measure, mass))
self.wait(2)
class ImagineShapesAsMetal(FourSelfSimilarShapes):
def construct(self):
titles = VGroup(*list(map(VGroup, self.get_titles())))
shapes = self.get_shapes(titles)
shapes.shift(DOWN)
descriptions = VGroup(*[
TextMobject(*words, arg_separator = "\\\\")
for shape, words in zip(shapes, [
["Thin", "wire"],
["Flat", "sheet"],
["Solid", "cube"],
["Sierpinski", "mesh"]
])
])
for title, description in zip(titles, descriptions):
description.move_to(title, UP)
title.target = description
self.add(titles, shapes)
for shape in shapes:
shape.generate_target()
shape.target.set_color(LIGHT_GREY)
shapes[-1].target.set_color_by_gradient(GREY, WHITE)
for shape, title in zip(shapes, titles):
self.play(
MoveToTarget(title),
MoveToTarget(shape)
)
self.wait()
self.wait()
for shape in shapes:
self.play(
shape.scale, 0.5, shape.get_top(),
run_time = 3,
rate_func = there_and_back
)
self.wait()
class ScaledLineMass(Scene):
CONFIG = {
"title" : "Line",
"mass_scaling_factor" : "\\frac{1}{2}",
"shape_width" : 2,
"break_up_factor" : 1.3,
"vert_distance" : 2,
"brace_direction" : DOWN,
"shape_to_shape_buff" : 2*LARGE_BUFF,
}
def construct(self):
title = TextMobject(self.title)
title.to_edge(UP)
scaling_factor_label = TextMobject(
"Scaling factor:", "$\\frac{1}{2}$"
)
scaling_factor_label[1].set_color(YELLOW)
scaling_factor_label.to_edge(LEFT).shift(UP)
mass_scaling_label = TextMobject(
"Mass scaling factor:", "$%s$"%self.mass_scaling_factor
)
mass_scaling_label[1].set_color(GREEN)
mass_scaling_label.next_to(
scaling_factor_label, DOWN,
aligned_edge = LEFT,
buff = LARGE_BUFF
)
shape = self.get_shape()
shape.set_width(self.shape_width)
shape.center()
shape.shift(FRAME_X_RADIUS*RIGHT/2 + self.vert_distance*UP)
big_brace = Brace(shape, self.brace_direction)
big_brace_text = big_brace.get_text("$1$")
shape_copy = shape.copy()
shape_copy.next_to(shape, DOWN, buff = self.shape_to_shape_buff)
shape_copy.scale_in_place(self.break_up_factor)
for submob in shape_copy:
submob.scale_in_place(1./self.break_up_factor)
little_brace = Brace(shape_copy[0], self.brace_direction)
little_brace_text = little_brace.get_text("$\\frac{1}{2}$")
self.add(title, scaling_factor_label, mass_scaling_label[0])
self.play(GrowFromCenter(shape))
self.play(
GrowFromCenter(big_brace),
Write(big_brace_text)
)
self.wait()
self.play(
shape.copy().replace, shape_copy[0]
)
self.remove(*self.get_mobjects_from_last_animation())
self.add(shape_copy[0])
self.play(
GrowFromCenter(little_brace),
Write(little_brace_text)
)
self.wait()
self.play(Write(mass_scaling_label[1], run_time = 1))
self.wait()
self.play(FadeIn(
VGroup(*shape_copy[1:]),
lag_ratio = 0.5
))
self.wait()
self.play(Transform(
shape_copy.copy(), shape
))
self.wait()
def get_shape(self):
return VGroup(
Line(LEFT, ORIGIN),
Line(ORIGIN, RIGHT)
).set_color(BLUE)
class ScaledSquareMass(ScaledLineMass):
CONFIG = {
"title" : "Square",
"mass_scaling_factor" : "\\frac{1}{4} = \\left( \\frac{1}{2} \\right)^2",
"brace_direction" : LEFT,
}
def get_shape(self):
return VGroup(*[
Square(
stroke_width = 0,
fill_color = BLUE,
fill_opacity = 0.7
).shift(vect)
for vect in compass_directions(start_vect = DOWN+LEFT)
])
class ScaledCubeMass(ScaledLineMass):
CONFIG = {
"title" : "Cube",
"mass_scaling_factor" : "\\frac{1}{8} = \\left( \\frac{1}{2} \\right)^3",
}
def get_shape(self):
return VectorizedPoint()
class FormCubeFromSubcubesIn3D(Scene):
def construct(self):
pass
class ScaledSierpinskiMass(ScaledLineMass):
CONFIG = {
"title" : "Sierpinski",
"mass_scaling_factor" : "\\frac{1}{3}",
"vert_distance" : 2.5,
"shape_to_shape_buff" : 1.5*LARGE_BUFF,
}
def get_shape(self):
return Sierpinski(order = 6)
class DefineTwoDimensional(PiCreatureScene):
CONFIG = {
"dimension" : "2",
"length_color" : GREEN,
"dimension_color" : YELLOW,
"shape_width" : 2,
"scale_factor" : 0.5,
"bottom_shape_buff" : MED_SMALL_BUFF,
"scalar" : "s",
}
def construct(self):
self.add_title()
self.add_h_line()
self.add_shape()
self.add_width_mass_labels()
self.show_top_length()
self.change_mode("thinking")
self.perform_scaling()
self.show_dimension()
def add_title(self):
title = TextMobject(
self.dimension, "-dimensional",
arg_separator = ""
)
self.dimension_in_title = title[0]
self.dimension_in_title.set_color(self.dimension_color)
title.to_edge(UP)
self.add(title)
self.title = title
def add_h_line(self):
self.h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
self.add(self.h_line)
def add_shape(self):
shape = self.get_shape()
shape.set_width(self.shape_width)
shape.next_to(self.title, DOWN, buff = MED_LARGE_BUFF)
# self.shape.shift(FRAME_Y_RADIUS*UP/2)
self.mass_color = shape.get_color()
self.add(shape)
self.shape = shape
def add_width_mass_labels(self):
top_length = TextMobject("Length:", "$L$")
top_mass = TextMobject("Mass:", "$M$")
bottom_length = TextMobject(
"Length: ", "$%s$"%self.scalar, "$L$",
arg_separator = ""
)
bottom_mass = TextMobject(
"Mass: ",
"$%s^%s$"%(self.scalar, self.dimension),
"$M$",
arg_separator = ""
)
self.dimension_in_exp = VGroup(
*bottom_mass[1][-len(self.dimension):]
)
self.dimension_in_exp.set_color(self.dimension_color)
top_group = VGroup(top_length, top_mass)
bottom_group = VGroup(bottom_length, bottom_mass)
for group in top_group, bottom_group:
group.arrange(
DOWN,
buff = MED_LARGE_BUFF,
aligned_edge = LEFT
)
group[0][-1].set_color(self.length_color)
group[1][-1].set_color(self.mass_color)
top_group.next_to(self.h_line, UP, buff = LARGE_BUFF)
bottom_group.next_to(self.h_line, DOWN, buff = LARGE_BUFF)
for group in top_group, bottom_group:
group.to_edge(LEFT)
self.add(top_group, bottom_group)
self.top_L = top_length[-1]
self.bottom_L = VGroup(*bottom_length[-2:])
self.bottom_mass = bottom_mass
def show_top_length(self):
brace = Brace(self.shape, LEFT)
top_L = self.top_L.copy()
self.play(GrowFromCenter(brace))
self.play(top_L.next_to, brace, LEFT)
self.wait()
self.brace = brace
def perform_scaling(self):
group = VGroup(self.shape, self.brace).copy()
self.play(
group.shift,
(group.get_top()[1]+self.bottom_shape_buff)*DOWN
)
shape, brace = group
bottom_L = self.bottom_L.copy()
shape.generate_target()
shape.target.scale_in_place(
self.scale_factor,
)
brace.target = Brace(shape.target, LEFT)
self.play(*list(map(MoveToTarget, group)))
self.play(bottom_L.next_to, brace, LEFT)
self.wait()
def show_dimension(self):
top_dimension = self.dimension_in_title.copy()
self.play(self.pi_creature.look_at, top_dimension)
self.play(Transform(
top_dimension,
self.dimension_in_exp,
run_time = 2,
))
self.wait(3)
def get_shape(self):
return Square(
stroke_width = 0,
fill_color = BLUE,
fill_opacity = 0.7,
)
class DefineThreeDimensional(DefineTwoDimensional):
CONFIG = {
"dimension" : "3",
}
def get_shape(self):
return Square(
stroke_width = 0,
fill_opacity = 0
)
class DefineSierpinskiDimension(DefineTwoDimensional):
CONFIG = {
"dimension" : "D",
"scalar" : "\\left( \\frac{1}{2} \\right)",
"sierpinski_order" : 6,
"equation_scale_factor" : 1.3,
}
def construct(self):
DefineTwoDimensional.construct(self)
self.change_mode("confused")
self.wait()
self.add_one_third()
self.isolate_equation()
def add_one_third(self):
equation = TextMobject(
"$= \\left(\\frac{1}{3}\\right)$", "$M$",
arg_separator = ""
)
equation.set_color_by_tex("$M$", self.mass_color)
equation.next_to(self.bottom_mass)
self.play(Write(equation))
self.change_mode("pondering")
self.wait()
self.equation = VGroup(self.bottom_mass, equation)
self.distilled_equation = VGroup(
self.bottom_mass[1],
equation[0]
).copy()
def isolate_equation(self):
# everything = VGroup(*self.get_mobjects())
keepers = [self.pi_creature, self.equation]
for mob in keepers:
mob.save_state()
keepers_copies = [mob.copy() for mob in keepers]
self.play(
*[
ApplyMethod(mob.fade, 0.5)
for mob in self.get_mobjects()
] + [
Animation(mob)
for mob in keepers_copies
]
)
self.remove(*keepers_copies)
for mob in keepers:
ApplyMethod(mob.restore).update(1)
self.add(*keepers)
self.play(
self.pi_creature.change_mode, "confused",
self.pi_creature.look_at, self.equation
)
self.wait()
equation = self.distilled_equation
self.play(
equation.arrange, RIGHT,
equation.scale, self.equation_scale_factor,
equation.to_corner, UP+RIGHT,
run_time = 2
)
self.wait(2)
simpler_equation = TexMobject("2^D = 3")
simpler_equation[1].set_color(self.dimension_color)
simpler_equation.scale(self.equation_scale_factor)
simpler_equation.next_to(equation, DOWN, buff = MED_LARGE_BUFF)
log_expression = TexMobject("\\log_2(3) \\approx", "1.585")
log_expression[-1].set_color(self.dimension_color)
log_expression.scale(self.equation_scale_factor)
log_expression.next_to(simpler_equation, DOWN, buff = MED_LARGE_BUFF)
log_expression.shift_onto_screen()
self.play(Write(simpler_equation))
self.change_mode("pondering")
self.wait(2)
self.play(Write(log_expression))
self.play(
self.pi_creature.change_mode, "hooray",
self.pi_creature.look_at, log_expression
)
self.wait(2)
def get_shape(self):
return Sierpinski(
order = self.sierpinski_order,
color = RED,
)
class ShowSierpinskiCurve(Scene):
CONFIG = {
"max_order" : 8,
}
def construct(self):
curve = self.get_curve(2)
self.play(ShowCreation(curve, run_time = 2))
for order in range(3, self.max_order + 1):
self.play(Transform(
curve, self.get_curve(order),
run_time = 2
))
self.wait()
def get_curve(self, order):
curve = SierpinskiCurve(order = order, monochromatic = True)
curve.set_color(RED)
return curve
class LengthAndAreaOfSierpinski(ShowSierpinskiCurve):
CONFIG = {
"curve_start_order" : 5,
"sierpinski_start_order" : 4,
"n_iterations" : 3,
}
def construct(self):
length = TextMobject("Length = $\\infty$")
length.shift(FRAME_X_RADIUS*LEFT/2).to_edge(UP)
area = TextMobject("Area = $0$")
area.shift(FRAME_X_RADIUS*RIGHT/2).to_edge(UP)
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
self.add(length, area, v_line)
curve = self.get_curve(order = self.curve_start_order)
sierp = self.get_sierpinski(order = self.sierpinski_start_order)
self.add(curve, sierp)
self.wait()
for x in range(self.n_iterations):
new_curve = self.get_curve(order = self.curve_start_order+x+1)
alpha = (x+1.0)/self.n_iterations
stroke_width = interpolate(3, 1, alpha)
new_curve.set_stroke(width = stroke_width)
new_sierp = self.get_sierpinski(
order = self.sierpinski_start_order+x+1
)
self.play(
Transform(curve, new_curve),
Transform(sierp, new_sierp),
run_time = 2
)
self.play(sierp.set_fill, None, 0)
self.wait()
def get_curve(self, order):
# curve = ShowSierpinskiCurve.get_curve(self, order)
curve = SierpinskiCurve(order = order)
curve.set_height(4).center()
curve.shift(FRAME_X_RADIUS*LEFT/2)
return curve
def get_sierpinski(self, order):
result = Sierpinski(order = order)
result.shift(FRAME_X_RADIUS*RIGHT/2)
return result
class FractionalAnalogOfLengthAndArea(Scene):
def construct(self):
last_sc = LengthAndAreaOfSierpinski(skip_animations = True)
self.add(*last_sc.get_mobjects())
morty = Mortimer()
morty.to_corner(DOWN+RIGHT)
self.play(FadeIn(morty))
self.play(PiCreatureSays(
morty,
"""
Better described with a
1.585-dimensional measure.
"""
))
self.play(Blink(morty))
self.wait()
class DimensionOfKoch(Scene):
CONFIG = {
"scaling_factor_color" : YELLOW,
"mass_scaling_color" : BLUE,
"dimension_color" : GREEN_A,
"curve_class" : KochCurve,
"scaling_factor" : 3,
"mass_scaling_factor" : 4,
"num_subparts" : 4,
"koch_curve_order" : 5,
"koch_curve_width" : 5,
"break_up_factor" : 1.5,
"down_shift" : 3*DOWN,
"dimension_rhs" : "\\approx 1.262",
}
def construct(self):
self.add_labels()
self.add_curve()
self.break_up_curve()
self.compare_sizes()
self.show_dimension()
def add_labels(self):
scaling_factor = TextMobject(
"Scaling factor:",
"$\\frac{1}{%d}$"%self.scaling_factor,
)
scaling_factor.next_to(ORIGIN, UP)
scaling_factor.to_edge(LEFT)
scaling_factor[1].set_color(self.scaling_factor_color)
self.add(scaling_factor[0])
mass_scaling = TextMobject(
"Mass scaling factor:",
"$\\frac{1}{%d}$"%self.mass_scaling_factor
)
mass_scaling.next_to(ORIGIN, DOWN)
mass_scaling.to_edge(LEFT)
mass_scaling[1].set_color(self.mass_scaling_color)
self.add(mass_scaling[0])
self.scaling_factor_mob = scaling_factor[1]
self.mass_scaling_factor_mob = mass_scaling[1]
def add_curve(self):
curve = self.curve_class(order = self.koch_curve_order)
curve.set_width(self.koch_curve_width)
curve.to_corner(UP+RIGHT, LARGE_BUFF)
self.play(ShowCreation(curve, run_time = 2))
self.wait()
self.curve = curve
def break_up_curve(self):
curve_copy = self.curve.copy()
length = len(curve_copy)
n_parts = self.num_subparts
broken_curve = VGroup(*[
VGroup(*curve_copy[i*length/n_parts:(i+1)*length/n_parts])
for i in range(n_parts)
])
self.play(broken_curve.shift, self.down_shift)
broken_curve.generate_target()
break_up(broken_curve.target, self.break_up_factor)
broken_curve.target.shift_onto_screen
self.play(MoveToTarget(broken_curve))
self.wait()
self.add(broken_curve)
self.broken_curve = broken_curve
def compare_sizes(self):
big_brace = Brace(self.curve, DOWN)
one = big_brace.get_text("$1$")
little_brace = Brace(self.broken_curve[0], DOWN)
one_third = little_brace.get_text("1/%d"%self.scaling_factor)
one_third.set_color(self.scaling_factor_color)
self.play(
GrowFromCenter(big_brace),
GrowFromCenter(little_brace),
Write(one),
Write(one_third),
)
self.wait()
self.play(Write(self.scaling_factor_mob))
self.wait()
self.play(Write(self.mass_scaling_factor_mob))
self.wait()
def show_dimension(self):
raw_formula = TexMobject("""
\\left( \\frac{1}{%s} \\right)^D
=
\\left( \\frac{1}{%s} \\right)
"""%(self.scaling_factor, self.mass_scaling_factor))
formula = VGroup(
VGroup(*raw_formula[:5]),
VGroup(raw_formula[5]),
VGroup(raw_formula[6]),
VGroup(*raw_formula[7:]),
)
formula.to_corner(UP+LEFT)
simpler_formula = TexMobject(
str(self.scaling_factor),
"^D", "=",
str(self.mass_scaling_factor)
)
simpler_formula.move_to(formula, UP)
for mob in formula, simpler_formula:
mob[0].set_color(self.scaling_factor_color)
mob[1].set_color(self.dimension_color)
mob[3].set_color(self.mass_scaling_color)
log_expression = TexMobject(
"D = \\log_%d(%d) %s"%(
self.scaling_factor,
self.mass_scaling_factor,
self.dimension_rhs
)
)
log_expression[0].set_color(self.dimension_color)
log_expression[5].set_color(self.scaling_factor_color)
log_expression[7].set_color(self.mass_scaling_color)
log_expression.next_to(
simpler_formula, DOWN,
aligned_edge = LEFT,
buff = MED_LARGE_BUFF
)
third = self.scaling_factor_mob.copy()
fourth = self.mass_scaling_factor_mob.copy()
for mob in third, fourth:
mob.add(VectorizedPoint(mob.get_right()))
mob.add_to_back(VectorizedPoint(mob.get_left()))
self.play(
Transform(third, formula[0]),
Transform(fourth, formula[-1]),
)
self.play(*list(map(FadeIn, formula[1:-1])))
self.remove(third, fourth)
self.add(formula)
self.wait(2)
self.play(Transform(formula, simpler_formula))
self.wait(2)
self.play(Write(log_expression))
self.wait(2)
class DimensionOfQuadraticKoch(DimensionOfKoch):
CONFIG = {
"curve_class" : QuadraticKoch,
"scaling_factor" : 4,
"mass_scaling_factor" : 8,
"num_subparts" : 8,
"koch_curve_order" : 4,
"koch_curve_width" : 4,
"break_up_factor" : 1.7,
"down_shift" : 4*DOWN,
"dimension_rhs" : "= \\frac{3}{2} = 1.5",
}
def construct(self):
self.add_labels()
self.add_curve()
self.set_color_curve_subparts()
self.show_dimension()
def get_curve(self, order):
curve = self.curve_class(
order = order,
monochromatic = True
)
curve.set_width(self.koch_curve_width)
alpha = float(order) / self.koch_curve_order
stroke_width = interpolate(3, 1, alpha)
curve.set_stroke(width = stroke_width)
return curve
def add_curve(self):
seed_label = TextMobject("Seed")
seed_label.shift(FRAME_X_RADIUS*RIGHT/2).to_edge(UP)
seed = self.get_curve(order = 1)
seed.next_to(seed_label, DOWN)
curve = seed.copy()
resulting_fractal = TextMobject("Resulting fractal")
resulting_fractal.shift(FRAME_X_RADIUS*RIGHT/2)
self.add(seed_label, seed)
self.wait()
self.play(
curve.next_to, resulting_fractal, DOWN, MED_LARGE_BUFF,
Write(resulting_fractal, run_time = 1)
)
for order in range(2, self.koch_curve_order+1):
new_curve = self.get_curve(order)
new_curve.move_to(curve)
n_curve_parts = curve.get_num_curves()
curve.insert_n_curves(6 * n_curve_parts)
curve.make_jagged()
self.play(Transform(curve, new_curve, run_time = 2))
self.wait()
self.curve = curve
def set_color_curve_subparts(self):
n_parts = self.num_subparts
colored_curve = self.curve_class(
order = self.koch_curve_order,
stroke_width = 1
)
colored_curve.replace(self.curve)
length = len(colored_curve)
broken_curve = VGroup(*[
VGroup(*colored_curve[i*length/n_parts:(i+1)*length/n_parts])
for i in range(n_parts)
])
colors = it.cycle([WHITE, RED])
for subpart, color in zip(broken_curve, colors):
subpart.set_color(color)
self.play(
FadeOut(self.curve),
FadeIn(colored_curve)
)
self.play(
ApplyFunction(
lambda m : break_up(m, self.break_up_factor),
broken_curve,
rate_func = there_and_back,
run_time = 2
)
)
self.wait()
self.play(Write(self.scaling_factor_mob))
self.play(Write(self.mass_scaling_factor_mob))
self.wait(2)
class ThisIsSelfSimilarityDimension(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
This is called
``self-similarity dimension''
""")
self.change_student_modes(*["pondering"]*3)
self.wait(2)
class ShowSeveralSelfSimilarityDimensions(Scene):
def construct(self):
vects = [
4*LEFT,
ORIGIN,
4*RIGHT,
]
fractal_classes = [
PentagonalFractal,
QuadraticKoch,
DiamondFractal,
]
max_orders = [
4,
4,
5,
]
dimensions = [
1.668,
1.500,
1.843,
]
title = TextMobject("``Self-similarity dimension''")
title.to_edge(UP)
title.set_color(YELLOW)
self.add(title)
def get_curves(order):
curves = VGroup()
for Class, vect in zip(fractal_classes, vects):
curve = Class(order = order)
curve.set_width(2),
curve.shift(vect)
curves.add(curve)
return curves
curves = get_curves(1)
self.add(curves)
for curve, dimension, u in zip(curves, dimensions, [1, -1, 1]):
label = TextMobject("%.3f-dimensional"%dimension)
label.scale(0.85)
label.next_to(curve, u*UP, buff = LARGE_BUFF)
self.add(label)
self.wait()
for order in range(2, max(max_orders)+1):
anims = []
for curve, max_order in zip(curves, max_orders):
if order <= max_order:
new_curve = curve.__class__(order = order)
new_curve.replace(curve)
anims.append(Transform(curve, new_curve))
self.play(*anims, run_time = 2)
self.wait()
self.curves = curves
class SeparateFractals(Scene):
def construct(self):
last_sc = ShowSeveralSelfSimilarityDimensions(skip_animations = True)
self.add(*last_sc.get_mobjects())
quad_koch = last_sc.curves[1]
length = len(quad_koch)
new_quad_koch = VGroup(*[
VGroup(*quad_koch[i*length/8:(i+1)*length/8])
for i in range(8)
])
curves = list(last_sc.curves)
curves[1] = new_quad_koch
curves = VGroup(*curves)
curves.save_state()
self.play(*[
ApplyFunction(
lambda m : break_up(m, 2),
curve
)
for curve in curves
])
self.wait(2)
self.play(curves.restore)
self.wait()
class ShowDiskScaling(Scene):
def construct(self):
self.show_non_self_similar_shapes()
self.isolate_disk()
self.scale_disk()
self.write_mass_scaling_factor()
self.try_fitting_small_disks()
def show_non_self_similar_shapes(self):
title = TextMobject(
"Most shapes are not self-similar"
)
title.to_edge(UP)
self.add(title)
hexagon = RegularPolygon(n = 6)
disk = Circle()
blob = VMobject().set_points_smoothly([
RIGHT, RIGHT+UP, ORIGIN, RIGHT+DOWN, LEFT, UP, RIGHT
])
britain = Britain()
shapes = VGroup(hexagon, blob, disk, britain)
for shape in shapes:
shape.set_width(1.5)
shape.set_stroke(width = 0)
shape.set_fill(opacity = 1)
shapes.set_color_by_gradient(BLUE_B, BLUE_E)
shapes.arrange(RIGHT, buff = LARGE_BUFF)
shapes.next_to(title, DOWN)
for shape in shapes:
self.play(FadeIn(shape))
self.wait(2)
self.disk = disk
self.to_fade = VGroup(
title, hexagon, blob, britain
)
def isolate_disk(self):
disk = self.disk
self.play(
FadeOut(self.to_fade),
disk.set_width, 2,
disk.next_to, ORIGIN, LEFT, 2,
disk.set_fill, BLUE_D, 0.7
)
radius = Line(
disk.get_center(), disk.get_right(),
color = YELLOW
)
one = TexMobject("1").next_to(radius, DOWN, SMALL_BUFF)
self.play(ShowCreation(radius))
self.play(Write(one))
self.wait()
self.disk.add(radius, one)
def scale_disk(self):
scaled_disk = self.disk.copy()
scaled_disk.generate_target()
scaled_disk.target.scale(2)
scaled_disk.target.next_to(ORIGIN, RIGHT)
one = scaled_disk.target[-1]
two = TexMobject("2")
two.move_to(one, UP)
scaled_disk.target.submobjects[-1] = two
self.play(MoveToTarget(scaled_disk))
self.wait()
self.scaled_disk = scaled_disk
def write_mass_scaling_factor(self):
mass_scaling = TextMobject(
"Mass scaling factor: $2^2 = 4$"
)
mass_scaling.next_to(self.scaled_disk, UP)
mass_scaling.to_edge(UP)
self.play(Write(mass_scaling))
self.wait()
def try_fitting_small_disks(self):
disk = self.disk.copy()
disk.submobjects = []
disk.set_fill(opacity = 0.5)
foursome = VGroup(*[
disk.copy().next_to(ORIGIN, vect, buff = 0)
for vect in compass_directions(start_vect = UP+RIGHT)
])
foursome.move_to(self.scaled_disk)
self.play(Transform(disk, foursome))
self.remove(*self.get_mobjects_from_last_animation())
self.add(foursome)
self.wait()
self.play(ApplyFunction(
lambda m : break_up(m, 0.2),
foursome,
rate_func = there_and_back,
run_time = 4,
))
self.wait()
self.play(FadeOut(foursome))
self.wait()
class WhatDoYouMeanByMass(TeacherStudentsScene):
def construct(self):
self.student_says(
"What do you mean \\\\ by mass?",
target_mode = "sassy"
)
self.change_student_modes("pondering", "sassy", "confused")
self.wait()
self.play(self.get_teacher().change_mode, "thinking")
self.wait(2)
self.teacher_thinks("")
self.zoom_in_on_thought_bubble()
class BoxCountingScene(Scene):
CONFIG = {
"box_width" : 0.25,
"box_color" : YELLOW,
"box_opacity" : 0.5,
"num_boundary_check_points" : 200,
"corner_rect_left_extension" : 0,
}
def setup(self):
self.num_rows = 2*int(FRAME_Y_RADIUS/self.box_width)+1
self.num_cols = 2*int(FRAME_X_RADIUS/self.box_width)+1
def get_grid(self):
v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS)
v_lines = VGroup(*[
v_line.copy().shift(u*x*self.box_width*RIGHT)
for x in range(self.num_cols/2+1)
for u in [-1, 1]
])
h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS)
h_lines = VGroup(*[
h_line.copy().shift(u*y*self.box_width*UP)
for y in range(self.num_rows/2+1)
for u in [-1, 1]
])
grid = VGroup(v_lines, h_lines)
if self.box_width > 0.2:
grid.set_stroke(width = 1)
else:
grid.set_stroke(width = 0.5)
return grid
def get_highlighted_boxes(self, vmobject):
points = []
if vmobject.stroke_width > 0:
for submob in vmobject.family_members_with_points():
alphas = np.linspace(0, 1, self.num_boundary_check_points)
points += [
submob.point_from_proportion(alpha)
for alpha in alphas
]
if vmobject.fill_opacity > 0:
camera = Camera(**LOW_QUALITY_CAMERA_CONFIG)
camera.capture_mobject(vmobject)
box_centers = self.get_box_centers()
pixel_coords = camera.points_to_pixel_coords(box_centers)
for index, (x, y) in enumerate(pixel_coords):
try:
rgb = camera.pixel_array[y, x]
if not np.all(rgb == np.zeros(3)):
points.append(box_centers[index])
except:
pass
return self.get_boxes(points)
def get_box_centers(self):
bottom_left = reduce(op.add, [
self.box_width*(self.num_cols/2)*LEFT,
self.box_width*(self.num_rows/2)*DOWN,
self.box_width*RIGHT/2,
self.box_width*UP/2,
])
return np.array([
bottom_left + (x*RIGHT+y*UP)*self.box_width
for x in range(self.num_cols)
for y in range(self.num_rows)
])
def get_boxes(self, points):
points = np.array(points)
rounded_points = np.floor(points/self.box_width)*self.box_width
unique_rounded_points = np.vstack({
tuple(row) for
row in rounded_points
})
return VGroup(*[
Square(
side_length = self.box_width,
stroke_width = 0,
fill_color = self.box_color,
fill_opacity = self.box_opacity,
).move_to(point, DOWN+LEFT)
for point in unique_rounded_points
])
def get_corner_rect(self):
rect = Rectangle(
height = FRAME_Y_RADIUS/2,
width = FRAME_X_RADIUS+self.corner_rect_left_extension,
stroke_width = 0,
fill_color = BLACK,
fill_opacity = 0.8
)
rect.to_corner(UP+RIGHT, buff = 0)
return rect
def get_counting_label(self):
label = TextMobject("Boxes touched:")
label.next_to(ORIGIN, RIGHT)
label.to_edge(UP)
label.shift(self.corner_rect_left_extension*LEFT)
self.counting_num_reference = label[-1]
rect = BackgroundRectangle(label)
rect.stretch(1.3, 0)
rect.move_to(label, LEFT)
label.add_to_back(rect)
return label
def count_boxes(self, boxes):
num = DecimalNumber(len(boxes), num_decimal_places = 0)
num.next_to(boxes, RIGHT)
num.add_to_back(BackgroundRectangle(num))
self.play(ShowCreation(boxes, run_time = 3))
self.play(Write(num))
self.play(
num.next_to, self.counting_num_reference, RIGHT, MED_SMALL_BUFF, DOWN,
num.set_color, YELLOW
)
return num
class BoxCountingWithDisk(BoxCountingScene):
CONFIG = {
"box_width" : 0.25,
"num_boundary_check_points" : 200,
"corner_rect_left_extension" : 2,
"disk_opacity" : 0.5,
"disk_stroke_width" : 0.5,
"decimal_string" : "= %.2f",
}
def construct(self):
disk = Circle(radius = 1)
disk.set_fill(BLUE, opacity = self.disk_opacity)
disk.set_stroke(BLUE, width = self.disk_stroke_width)
disk.shift(0.1*np.sqrt(2)*(UP+RIGHT))
radius = Line(disk.get_center(), disk.get_right())
disk.add(radius)
one = TexMobject("1").next_to(radius, DOWN, SMALL_BUFF)
boxes = self.get_highlighted_boxes(disk)
small_box_num = len(boxes)
grid = self.get_grid()
corner_rect = self.get_corner_rect()
counting_label = self.get_counting_label()
prop_words = TextMobject("Proportional to", "$\\pi r^2$")
prop_words[1].set_color(BLUE)
prop_words.next_to(counting_label, DOWN, aligned_edge = LEFT)
self.add(disk, one)
self.play(
ShowCreation(grid),
Animation(disk),
)
self.wait()
self.play(
FadeIn(corner_rect),
FadeIn(counting_label)
)
counting_mob = self.count_boxes(boxes)
self.wait()
self.play(Write(prop_words, run_time = 2))
self.wait(2)
self.play(FadeOut(prop_words))
disk.generate_target()
disk.target.scale(2, about_point = disk.get_top())
two = TexMobject("2").next_to(disk.target[1], DOWN, SMALL_BUFF)
self.play(
MoveToTarget(disk),
Transform(one, two),
FadeOut(boxes),
)
self.play(counting_mob.next_to, counting_mob, DOWN)
boxes = self.get_highlighted_boxes(disk)
large_box_count = len(boxes)
new_counting_mob = self.count_boxes(boxes)
self.wait()
frac_line = TexMobject("-")
frac_line.set_color(YELLOW)
frac_line.stretch_to_fit_width(new_counting_mob.get_width())
frac_line.next_to(new_counting_mob, DOWN, buff = SMALL_BUFF)
decimal = TexMobject(self.decimal_string%(float(large_box_count)/small_box_num))
decimal.next_to(frac_line, RIGHT)
approx = TexMobject("\\approx 2^2")
approx.next_to(decimal, RIGHT, aligned_edge = DOWN)
approx.shift_onto_screen()
self.play(*list(map(Write, [frac_line, decimal])))
self.play(Write(approx))
self.wait()
randy = Randolph().shift(3*RIGHT).to_edge(DOWN)
self.play(FadeIn(randy))
self.play(PiCreatureSays(
randy, "Is it?",
target_mode = "sassy",
bubble_kwargs = {"direction" : LEFT}
))
self.play(Blink(randy))
self.wait()
class FinerBoxCountingWithDisk(BoxCountingWithDisk):
CONFIG = {
"box_width" : 0.03,
"num_boundary_check_points" : 1000,
"disk_stroke_width" : 0.5,
"decimal_string" : "= %.2f",
}
class PlotDiskBoxCounting(GraphScene):
CONFIG = {
"x_axis_label" : "Scaling factor",
"y_axis_label" : "Number of boxes \\\\ touched",
"x_labeled_nums" : [],
"y_labeled_nums" : [],
"x_min" : 0,
"y_min" : 0,
"y_max" : 30,
"func" : lambda x : 0.5*x**2,
"func_label" : "f(x) = cx^2",
}
def construct(self):
self.plot_points()
self.describe_better_fit()
def plot_points(self):
self.setup_axes()
self.graph_function(self.func)
self.remove(self.graph)
data_points = [
self.input_to_graph_point(x) + ((random.random()-0.5)/x)*UP
for x in np.arange(2, 10, 0.5)
]
data_dots = VGroup(*[
Dot(point, radius = 0.05, color = YELLOW)
for point in data_points
])
self.play(ShowCreation(data_dots))
self.wait()
self.play(ShowCreation(self.graph))
self.label_graph(
self.graph,
self.func_label,
direction = RIGHT+DOWN,
buff = SMALL_BUFF,
color = WHITE,
)
self.wait()
def describe_better_fit(self):
words = TextMobject("Better fit at \\\\ higher inputs")
arrow = Arrow(2*LEFT, 2*RIGHT)
arrow.next_to(self.x_axis_label_mob, UP)
arrow.shift(2*LEFT)
words.next_to(arrow, UP)
self.play(ShowCreation(arrow))
self.play(Write(words))
self.wait(2)
class FineGridSameAsLargeScaling(BoxCountingScene):
CONFIG = {
"box_width" : 0.25/6,
"scale_factor" : 6
}
def construct(self):
disk = Circle(radius = 1)
disk.set_fill(BLUE, opacity = 0.5)
disk.set_stroke(BLUE, width = 1)
grid = self.get_grid()
grid.scale(self.scale_factor)
self.add(grid, disk)
self.wait()
self.play(disk.scale, self.scale_factor)
self.wait()
self.play(
grid.scale, 1./self.scale_factor,
disk.scale, 1./self.scale_factor,
disk.set_stroke, None, 0.5,
)
self.wait()
boxes = self.get_highlighted_boxes(disk)
self.play(ShowCreation(boxes, run_time = 3))
self.wait(2)
class BoxCountingSierpinski(BoxCountingScene):
CONFIG = {
"box_width" : 0.1,
"sierpinski_order" : 7,
"sierpinski_width" : 3,
"num_boundary_check_points" : 6,
"corner_rect_left_extension" : 2,
}
def construct(self):
self.add(self.get_grid())
sierp = Sierpinski(order = self.sierpinski_order)
sierp.set_fill(opacity = 0)
sierp.move_to(3*DOWN, DOWN+RIGHT)
sierp.set_width(self.sierpinski_width)
boxes = self.get_highlighted_boxes(sierp)
corner_rect = self.get_corner_rect()
counting_label = self.get_counting_label()
self.play(ShowCreation(sierp))
self.play(*list(map(FadeIn, [corner_rect, counting_label])))
self.wait()
counting_mob = self.count_boxes(boxes)
self.wait()
self.play(
FadeOut(boxes),
sierp.scale, 2, sierp.get_corner(DOWN+RIGHT),
)
self.play(counting_mob.next_to, counting_mob, DOWN)
boxes = self.get_highlighted_boxes(sierp)
new_counting_mob = self.count_boxes(boxes)
self.wait()
frac_line = TexMobject("-")
frac_line.set_color(YELLOW)
frac_line.stretch_to_fit_width(new_counting_mob.get_width())
frac_line.next_to(new_counting_mob, DOWN, buff = SMALL_BUFF)
approx_three = TexMobject("\\approx 3")
approx_three.next_to(frac_line, RIGHT)
equals_exp = TexMobject("= 2^{1.585...}")
equals_exp.next_to(approx_three, RIGHT, aligned_edge = DOWN)
equals_exp.shift_onto_screen()
self.play(*list(map(Write, [frac_line, approx_three])))
self.wait()
self.play(Write(equals_exp))
self.wait()
class PlotSierpinskiBoxCounting(PlotDiskBoxCounting):
CONFIG = {
"func" : lambda x : 0.5*x**1.585,
"func_label" : "f(x) = cx^{1.585}",
}
def construct(self):
self.plot_points()
class BoxCountingWithBritain(BoxCountingScene):
CONFIG = {
"box_width" : 0.1,
"num_boundary_check_points" : 5000,
"corner_rect_left_extension" : 1,
}
def construct(self):
self.show_box_counting()
self.show_formula()
def show_box_counting(self):
self.add(self.get_grid())
britain = Britain(
stroke_width = 2,
fill_opacity = 0
)
britain = fractalify(britain, order = 1, dimension = 1.21)
britain.shift(DOWN+LEFT)
boxes = self.get_highlighted_boxes(britain)
self.play(ShowCreation(britain, run_time = 3))
self.wait()
self.play(ShowCreation(boxes, run_time = 3))
self.wait()
self.play(FadeOut(boxes))
self.play(britain.scale, 2.5, britain.get_corner(DOWN+RIGHT))
boxes = self.get_highlighted_boxes(britain)
self.play(ShowCreation(boxes, run_time = 2))
self.wait()
def show_formula(self):
corner_rect = self.get_corner_rect()
equation = TextMobject("""
Number of boxes $\\approx$
\\quad $c(\\text{scaling factor})^{1.21}$
""")
equation.next_to(
corner_rect.get_corner(UP+LEFT), DOWN+RIGHT
)
N = equation[0].copy()
word_len = len("Numberofboxes")
approx = equation[word_len].copy()
c = equation[word_len+1].copy()
s = equation[word_len+3].copy()
dim = VGroup(*equation[-len("1.21"):]).copy()
N.set_color(YELLOW)
s.set_color(BLUE)
dim.set_color(GREEN)
simpler_eq = VGroup(N, approx, c, s, dim)
simpler_eq.generate_target()
simpler_eq.target.arrange(buff = SMALL_BUFF)
simpler_eq.target.move_to(N, LEFT)
simpler_eq.target[-1].next_to(
simpler_eq.target[-2].get_corner(UP+RIGHT),
RIGHT,
buff = SMALL_BUFF
)
self.play(
FadeIn(corner_rect),
Write(equation)
)
self.wait(2)
self.play(FadeIn(simpler_eq))
self.wait()
self.play(
FadeOut(equation),
Animation(simpler_eq)
)
self.play(MoveToTarget(simpler_eq))
self.wait(2)
log_expression1 = TexMobject(
"\\log(", "N", ")", "=",
"\\log(", "c", "s", "^{1.21}", ")"
)
log_expression2 = TexMobject(
"\\log(", "N", ")", "=",
"\\log(", "c", ")", "+",
"1.21", "\\log(", "s", ")"
)
for log_expression in log_expression1, log_expression2:
log_expression.next_to(simpler_eq, DOWN, aligned_edge = LEFT)
log_expression.set_color_by_tex("N", N.get_color())
log_expression.set_color_by_tex("s", s.get_color())
log_expression.set_color_by_tex("^{1.21}", dim.get_color())
log_expression.set_color_by_tex("1.21", dim.get_color())
rewired_log_expression1 = VGroup(*[
log_expression1[index].copy()
for index in [
0, 1, 2, 3, #match with log_expression2
4, 5, 8, 8,
7, 4, 6, 8
]
])
self.play(Write(log_expression1))
self.remove(log_expression1)
self.add(rewired_log_expression1)
self.wait()
self.play(Transform(
rewired_log_expression1,
log_expression2,
run_time = 2
))
self.wait(2)
self.final_expression = VGroup(
simpler_eq, rewired_log_expression1
)
class GiveShapeAndPonder(Scene):
def construct(self):
morty = Mortimer()
randy = Randolph()
morty.next_to(ORIGIN, DOWN).shift(3*RIGHT)
randy.next_to(ORIGIN, DOWN).shift(3*LEFT)
norway = Norway(fill_opacity = 0, stroke_width = 1)
norway.set_width(2)
norway.next_to(morty, UP+LEFT, buff = -MED_SMALL_BUFF)
self.play(
morty.change_mode, "raise_right_hand",
morty.look_at, norway,
randy.look_at, norway,
ShowCreation(norway)
)
self.play(Blink(morty))
self.play(randy.change_mode, "pondering")
self.play(Blink(randy))
self.wait()
class CheapBoxCountingWithBritain(BoxCountingWithBritain):
CONFIG = {
"skip_animations" : True,
}
def construct(self):
self.show_formula()
class ConfusedAtParabolicData(PlotDiskBoxCounting):
CONFIG = {
"func" : lambda x : 0.5*x**1.6,
"func_label" : "f(x) = cx^{1.21}",
}
def construct(self):
self.plot_points()
randy = Randolph()
randy.to_corner(DOWN+LEFT)
randy.shift(RIGHT)
self.play(FadeIn(randy))
self.play(randy.change_mode, "confused")
self.play(randy.look_at, self.x_axis_label_mob)
self.play(Blink(randy))
self.wait(2)
class IntroduceLogLogPlot(GraphScene):
CONFIG = {
"x_axis_label" : "\\log(s)",
"y_axis_label" : "\\log(N)",
"x_labeled_nums" : [],
"y_labeled_nums" : [],
"graph_origin" : 2.5*DOWN+6*LEFT,
"dimension" : 1.21,
"y_intercept" : 2,
"x_max" : 16,
}
def construct(self):
last_scene = CheapBoxCountingWithBritain()
expression = last_scene.final_expression
box = Rectangle(
stroke_color = WHITE,
fill_color = BLACK,
fill_opacity = 0.7,
)
box.replace(expression, stretch = True)
box.scale_in_place(1.2)
expression.add_to_back(box)
self.add(expression)
self.setup_axes(animate = False)
self.x_axis_label_mob[-2].set_color(BLUE)
self.y_axis_label_mob[-2].set_color(YELLOW)
graph = self.graph_function(
lambda x : self.y_intercept+self.dimension*x
)
self.remove(graph)
p1 = self.input_to_graph_point(2)
p2 = self.input_to_graph_point(3)
interim_point = p2[0]*RIGHT + p1[1]*UP
h_line = Line(p1, interim_point)
v_line = Line(interim_point, p2)
slope_lines = VGroup(h_line, v_line)
slope_lines.set_color(GREEN)
slope = TextMobject("Slope = ", "$%.2f$"%self.dimension)
slope[-1].set_color(GREEN)
slope.next_to(slope_lines, RIGHT)
self.wait()
data_points = [
self.input_to_graph_point(x) + ((random.random()-0.5)/x)*UP
for x in np.arange(1, 8, 0.7)
]
data_dots = VGroup(*[
Dot(point, radius = 0.05, color = YELLOW)
for point in data_points
])
self.play(ShowCreation(data_dots, run_time = 3))
self.wait()
self.play(
ShowCreation(graph),
Animation(expression)
)
self.wait()
self.play(ShowCreation(slope_lines))
self.play(Write(slope))
self.wait()
class ManyBritainCounts(BoxCountingWithBritain):
CONFIG = {
"box_width" : 0.1,
"num_boundary_check_points" : 10000,
"corner_rect_left_extension" : 1,
}
def construct(self):
britain = Britain(
stroke_width = 2,
fill_opacity = 0
)
britain = fractalify(britain, order = 1, dimension = 1.21)
britain.next_to(ORIGIN, LEFT)
self.add(self.get_grid())
self.add(britain)
for x in range(5):
self.play(britain.scale, 2, britain.point_from_proportion(0.8))
boxes = self.get_highlighted_boxes(britain)
self.play(ShowCreation(boxes))
self.wait()
self.play(FadeOut(boxes))
class ReadyForRealDefinition(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
Now for what
fractals really are.
""")
self.change_student_modes(*["hooray"]*3)
self.wait(2)
class DefineFractal(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
Fractals are shapes
with a non-integer dimension.
""")
self.change_student_modes("thinking", "happy", "erm")
self.wait(3)
self.teacher_says(
"Kind of...",
target_mode = "sassy"
)
self.change_student_modes(*["pondering"]*3)
self.play(*[
ApplyMethod(pi.look, DOWN)
for pi in self.get_pi_creatures()
])
self.wait(3)
class RoughnessAndFractionalDimension(Scene):
def construct(self):
title = TextMobject(
"Non-integer dimension $\\Leftrightarrow$ Roughness"
)
title.to_edge(UP)
self.add(title)
randy = Randolph().scale(2)
randy.to_corner(DOWN+RIGHT)
self.add(randy)
target = randy.copy()
target.change_mode("hooray")
ponder_target = randy.copy()
ponder_target.change_mode("pondering")
for mob in target, ponder_target:
fractalify(mob, order = 2)
dimension_label = TextMobject("Boundary dimension = ", "1")
dimension_label.to_edge(LEFT)
one = dimension_label[1]
one.set_color(BLUE)
new_dim = TexMobject("1.2")
new_dim.move_to(one, DOWN+LEFT)
new_dim.set_color(one.get_color())
self.add(dimension_label)
self.play(Blink(randy))
self.play(
Transform(randy, target, run_time = 2),
Transform(one, new_dim)
)
self.wait()
self.play(Blink(randy))
self.play(randy.look, DOWN+RIGHT)
self.wait()
self.play(randy.look, DOWN+LEFT)
self.play(Blink(randy))
self.wait()
self.play(Transform(randy, ponder_target))
self.wait()
class DifferentSlopesAtDifferentScales(IntroduceLogLogPlot):
def construct(self):
self.setup_axes(animate = False)
self.x_axis_label_mob[-2].set_color(BLUE)
self.y_axis_label_mob[-2].set_color(YELLOW)
self.graph_function(
lambda x : 0.01*(x-5)**3 + 0.3*x + 3
)
self.remove(self.graph)
words = TextMobject("""
Different slopes
at different scales
""")
words.to_edge(RIGHT)
arrows = VGroup(*[
Arrow(words.get_left(), self.input_to_graph_point(x))
for x in (1, 7, 12)
])
data_points = [
self.input_to_graph_point(x) + (0.3*(random.random()-0.5))*UP
for x in np.arange(1, self.x_max, 0.7)
]
data_dots = VGroup(*[
Dot(point, radius = 0.05, color = YELLOW)
for point in data_points
])
self.play(ShowCreation(data_dots, run_time = 2))
self.play(ShowCreation(self.graph))
self.wait()
self.play(
Write(words),
ShowCreation(arrows)
)
self.wait()
class HoldUpCoilExample(TeacherStudentsScene):
def construct(self):
point = UP+RIGHT
self.play(
self.get_teacher().change_mode, "raise_right_hand",
self.get_teacher().look_at, point
)
self.play(*[
ApplyMethod(pi.look_at, point)
for pi in self.get_students()
])
self.wait(5)
self.change_student_modes(*["thinking"]*3)
self.play(*[
ApplyMethod(pi.look_at, point)
for pi in self.get_students()
])
self.wait(5)
class SmoothHilbertZoom(Scene):
def construct(self):
hilbert = HilbertCurve(
order = 7,
color = MAROON_B,
monochromatic = True
)
hilbert.make_smooth()
self.add(hilbert)
two_d_title = TextMobject("2D at a distance...")
one_d_title = TextMobject("1D up close")
for title in two_d_title, one_d_title:
title.to_edge(UP)
self.add(two_d_title)
self.wait()
self.play(
ApplyMethod(
hilbert.scale, 100,
hilbert.point_from_proportion(0.3),
),
Transform(
two_d_title, one_d_title,
rate_func = squish_rate_func(smooth)
),
run_time = 3
)
self.wait()
class ListDimensionTypes(PiCreatureScene):
CONFIG = {
"use_morty" : False,
}
def construct(self):
types = VGroup(*list(map(TextMobject, [
"Box counting dimension",
"Information dimension",
"Hausdorff dimension",
"Packing dimension"
])))
types.arrange(DOWN, aligned_edge = LEFT)
for text in types:
self.play(
Write(text, run_time = 1),
self.pi_creature.change_mode, "pondering"
)
self.wait(3)
class ZoomInOnBritain(Scene):
CONFIG = {
"zoom_factor" : 1000
}
def construct(self):
britain = Britain()
fractalify(britain, order = 3, dimension = 1.21)
anchors = britain.get_anchors()
key_value = int(0.3*len(anchors))
point = anchors[key_value]
thinning_factor = 100
num_neighbors_kept = 1000
britain.set_points_as_corners(reduce(
lambda a1, a2 : np.append(a1, a2, axis = 0),
[
anchors[:key_value-num_neighbors_kept:thinning_factor,:],
anchors[key_value-num_neighbors_kept:key_value+num_neighbors_kept,:],
anchors[key_value+num_neighbors_kept::thinning_factor,:],
]
))
self.add(britain)
self.wait()
self.play(
britain.scale, self.zoom_factor, point,
run_time = 10
)
self.wait()
class NoteTheConstantSlope(Scene):
def construct(self):
words = TextMobject("Note the \\\\ constant slope")
words.set_color(YELLOW)
self.play(Write(words))
self.wait(2)
class FromHandwavyToQuantitative(Scene):
def construct(self):
randy = Randolph()
morty = Mortimer()
for pi in randy, morty:
pi.next_to(ORIGIN, DOWN)
randy.shift(2*LEFT)
morty.shift(2*RIGHT)
randy.make_eye_contact(morty)
self.add(randy, morty)
self.play(PiCreatureSays(
randy, "Fractals are rough",
target_mode = "shruggie"
))
self.play(morty.change_mode, "sassy")
self.play(Blink(morty))
self.play(
PiCreatureSays(
morty, "We can make \\\\ that quantitative!",
target_mode = "hooray"
),
FadeOut(randy.bubble),
FadeOut(randy.bubble.content),
randy.change_mode, "happy"
)
self.play(Blink(randy))
self.wait()
class WhatSlopeDoesLogLogPlotApproach(IntroduceLogLogPlot):
CONFIG = {
"words" : "What slope does \\\\ this approach?",
"x_max" : 20,
"y_max" : 15,
}
def construct(self):
self.setup_axes(animate = False)
self.x_axis_label_mob[-2].set_color(BLUE)
self.y_axis_label_mob[-2].set_color(YELLOW)
spacing = 0.5
x_range = np.arange(1, self.x_max, spacing)
randomness = [
0.5*np.exp(-x/2)+spacing*(0.8 + random.random()/(x**(0.5)))
for x in x_range
]
cum_sums = np.cumsum(randomness)
data_points = [
self.coords_to_point(x, cum_sum)
for x, cum_sum in zip(x_range, cum_sums)
]
data_dots = VGroup(*[
Dot(point, radius = 0.025, color = YELLOW)
for point in data_points
])
words = TextMobject(self.words)
p1, p2 = [
data_dots[int(alpha*len(data_dots))].get_center()
for alpha in (0.3, 0.5)
]
words.rotate(Line(p1, p2).get_angle())
words.next_to(p1, RIGHT, aligned_edge = DOWN, buff = 1.5)
morty = Mortimer()
morty.to_corner(DOWN+RIGHT)
self.add(morty)
self.play(ShowCreation(data_dots, run_time = 7))
self.play(
Write(words),
morty.change_mode, "speaking"
)
self.play(Blink(morty))
self.wait()
class BritainBoxCountHighZoom(BoxCountingWithBritain):
def construct(self):
britain = Britain(
stroke_width = 2,
fill_opacity = 0
)
britain = fractalify(britain, order = 2, dimension = 1.21)
self.add(self.get_grid())
self.add(britain)
for x in range(2):
self.play(
britain.scale, 10, britain.point_from_proportion(0.3),
run_time = 2
)
if x == 0:
a, b = 0.2, 0.5
else:
a, b = 0.25, 0.35
britain.pointwise_become_partial(britain, a, b)
self.count_britain(britain)
self.wait()
def count_britain(self, britain):
boxes = self.get_highlighted_boxes(britain)
self.play(ShowCreation(boxes))
self.wait()
self.play(FadeOut(boxes))
class IfBritainWasEventuallySmooth(Scene):
def construct(self):
britain = Britain()
britain.make_smooth()
point = britain.point_from_proportion(0.3)
self.add(britain)
self.wait()
self.play(
britain.scale, 200, point,
run_time = 10
)
self.wait()
class SmoothBritainLogLogPlot(IntroduceLogLogPlot):
CONFIG = {
}
def construct(self):
self.setup_axes()
self.graph_function(
lambda x : (1 + np.exp(-x/5.0))*x
)
self.remove(self.graph)
p1, p2, p3, p4 = [
self.input_to_graph_point(x)
for x in (1, 2, 7, 8)
]
interim_point1 = p2[0]*RIGHT + p1[1]*UP
interim_point2 = p4[0]*RIGHT + p3[1]*UP
print(self.func(2))
slope_lines1, slope_lines2 = VMobject(), VMobject()
slope_lines1.set_points_as_corners(
[p1, interim_point1, p2]
)
slope_lines2.set_points_as_corners(
[p3, interim_point2, p4]
)
slope_lines_group = VGroup(slope_lines1, slope_lines2)
slope_lines_group.set_color(GREEN)
slope_label1 = TextMobject("Slope $> 1$")
slope_label2 = TextMobject("Slope $= 1$")
slope_label1.next_to(slope_lines1)
slope_label2.next_to(slope_lines2)
data_points = [
self.input_to_graph_point(x) + ((random.random()-0.5)/x)*UP
for x in np.arange(1, 12, 0.7)
]
data_dots = VGroup(*[
Dot(point, radius = 0.05, color = YELLOW)
for point in data_points
])
self.play(ShowCreation(data_dots, run_time = 3))
self.play(ShowCreation(self.graph))
self.wait()
self.play(
ShowCreation(slope_lines1),
Write(slope_label1)
)
self.wait()
self.play(
ShowCreation(slope_lines2),
Write(slope_label2)
)
self.wait()
class SlopeAlwaysAboveOne(WhatSlopeDoesLogLogPlotApproach):
CONFIG = {
"words" : "Slope always $> 1$",
"x_max" : 20,
}
class ChangeWorldview(TeacherStudentsScene):
def construct(self):
self.teacher_says("""
This changes how
you see the world.
""")
self.change_student_modes(*["thinking"]*3)
self.wait(3)
class CompareBritainAndNorway(Scene):
def construct(self):
norway = Norway(
fill_opacity = 0,
stroke_width = 2,
)
norway.to_corner(UP+RIGHT, buff = 0)
fractalify(norway, order = 1, dimension = 1.5)
anchors = list(norway.get_anchors())
anchors.append(FRAME_X_RADIUS*RIGHT+FRAME_Y_RADIUS*UP)
norway.set_points_as_corners(anchors)
britain = Britain(
fill_opacity = 0,
stroke_width = 2
)
britain.shift(FRAME_X_RADIUS*LEFT/2)
britain.to_edge(UP)
fractalify(britain, order = 1, dimension = 1.21)
britain_label = TextMobject("""
Britain coast:
1.21-dimensional
""")
norway_label = TextMobject("""
Norway coast:
1.52-dimensional
""")
britain_label.next_to(britain, DOWN)
norway_label.next_to(britain_label, RIGHT, aligned_edge = DOWN)
norway_label.to_edge(RIGHT)
self.add(britain_label, norway_label)
self.play(
*list(map(ShowCreation, [norway, britain])),
run_time = 3
)
self.wait()
self.play(*it.chain(*[
[
mob.set_stroke, None, 0,
mob.set_fill, BLUE, 1
]
for mob in (britain, norway)
]))
self.wait(2)
class CompareOceansLabels(Scene):
def construct(self):
label1 = TextMobject("Dimension $\\approx 2.05$")
label2 = TextMobject("Dimension $\\approx 2.3$")
label1.shift(FRAME_X_RADIUS*LEFT/2).to_edge(UP)
label2.shift(FRAME_X_RADIUS*RIGHT/2).to_edge(UP)
self.play(Write(label1))
self.wait()
self.play(Write(label2))
self.wait()
class CompareOceans(Scene):
def construct(self):
pass
class FractalNonFractalFlowChart(Scene):
def construct(self):
is_fractal = TextMobject("Is it a \\\\ fractal?")
nature = TextMobject("Probably from \\\\ nature")
man_made = TextMobject("Probably \\\\ man-made")
is_fractal.to_edge(UP)
nature.shift(FRAME_X_RADIUS*LEFT/2)
man_made.shift(FRAME_X_RADIUS*RIGHT/2)
yes_arrow = Arrow(
is_fractal.get_bottom(),
nature.get_top()
)
no_arrow = Arrow(
is_fractal.get_bottom(),
man_made.get_top()
)
yes = TextMobject("Yes")
no = TextMobject("No")
yes.set_color(GREEN)
no.set_color(RED)
for word, arrow in (yes, yes_arrow), (no, no_arrow):
word.next_to(ORIGIN, UP)
word.rotate(arrow.get_angle())
if word is yes:
word.rotate(np.pi)
word.shift(arrow.get_center())
britain = Britain()
britain.set_height(3)
britain.to_corner(UP+LEFT)
self.add(britain)
randy = Randolph()
randy.set_height(3)
randy.to_corner(UP+RIGHT)
self.add(randy)
self.add(is_fractal)
self.wait()
for word, arrow, answer in (yes, yes_arrow, nature), (no, no_arrow, man_made):
self.play(
ShowCreation(arrow),
Write(word, run_time = 1)
)
self.play(Write(answer, run_time = 1))
if word is yes:
self.wait()
else:
self.play(Blink(randy))
class ShowPiCreatureFractalCreation(FractalCreation):
CONFIG = {
"fractal_class" : PentagonalPiCreatureFractal,
"max_order" : 4,
}
class FractalPatreonThanks(PatreonThanks):
CONFIG = {
"specific_patrons" : [
"Meshal Alshammari",
"Ali Yahya",
"CrypticSwarm ",
"Yu Jun",
"Shelby Doolittle",
"Dave Nicponski",
"Damion Kistler",
"Juan Batiz-Benet",
"Othman Alikhan",
"Markus Persson",
"Dan Buchoff",
"Derek Dai",
"Joseph John Cox",
"Luc Ritchie",
"Jerry Ling",
"Mark Govea",
"Guido Gambardella",
"Vecht ",
"Jonathan Eppele",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Kirk Werklund",
"Ripta Pasay",
"Felipe Diniz",
]
}
class AffirmLogo(SVGMobject):
CONFIG = {
"fill_color" : "#0FA0EA",
"fill_opacity" : 1,
"stroke_color" : "#0FA0EA",
"stroke_width" : 0,
"file_name" : "affirm_logo",
"width" : 3,
}
def __init__(self, **kwargs):
SVGMobject.__init__(self, **kwargs)
self.set_width(self.width)
class MortyLookingAtRectangle(Scene):
def construct(self):
morty = Mortimer()
morty.to_corner(DOWN+RIGHT)
url = TextMobject("affirmjobs.3b1b.co")
url.to_corner(UP+LEFT)
rect = Rectangle(height = 9, width = 16)
rect.set_height(5)
rect.next_to(url, DOWN)
rect.shift_onto_screen()
url.save_state()
url.next_to(morty.get_corner(UP+LEFT), UP)
affirm_logo = AffirmLogo()[0]
affirm_logo.to_corner(UP+RIGHT, buff = MED_LARGE_BUFF)
affirm_logo.shift(0.5*DOWN)
self.add(morty)
affirm_logo.save_state()
affirm_logo.shift(DOWN)
affirm_logo.set_fill(opacity = 0)
self.play(
ApplyMethod(affirm_logo.restore, run_time = 2),
morty.look_at, affirm_logo,
)
self.play(
morty.change_mode, "raise_right_hand",
morty.look_at, url,
)
self.play(Write(url))
self.play(Blink(morty))
self.wait()
self.play(
url.restore,
morty.change_mode, "happy"
)
self.play(ShowCreation(rect))
self.wait()
self.play(Blink(morty))
for mode in ["wave_2", "hooray", "happy", "pondering", "happy"]:
self.play(morty.change_mode, mode)
self.wait(2)
self.play(Blink(morty))
self.wait(2)
class Thumbnail(Scene):
def construct(self):
title = TextMobject("1.5-dimensional")
title.scale(2)
title.to_edge(UP)
koch_curve = QuadraticKoch(order = 6, monochromatic = True)
koch_curve.set_stroke(width = 0)
koch_curve.set_fill(BLUE)
koch_curve.set_height(1.5*FRAME_Y_RADIUS)
koch_curve.to_edge(DOWN, buff = SMALL_BUFF)
self.add(koch_curve, title)