diff --git a/constants.py b/constants.py index 8d601c52..cec1db7e 100644 --- a/constants.py +++ b/constants.py @@ -1,8 +1,8 @@ import os import numpy as np -DEFAULT_HEIGHT = 1440 -DEFAULT_WIDTH = 2560 +DEFAULT_HEIGHT = 1080 +DEFAULT_WIDTH = 1920 DEFAULT_FRAME_DURATION = 0.04 #There might be other configuration than pixel_shape later... @@ -15,7 +15,7 @@ MEDIUM_QUALITY_CAMERA_CONFIG = { } LOW_QUALITY_CAMERA_CONFIG = { - "pixel_shape" : (576, 1024), + "pixel_shape" : (480, 720), } diff --git a/mobject/mobject.py b/mobject/mobject.py index 64ebc78c..43f83231 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -408,7 +408,7 @@ class Mobject(object): lambda m : m.get_num_points() > 0, self.submobject_family() ) - + def arrange_submobjects(self, direction = RIGHT, buff = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, @@ -505,7 +505,8 @@ class Mobject(object): def repeat_submobject(self, submob): return submob.copy() - def interpolate(self, mobject1, mobject2, alpha, path_func): + def interpolate(self, mobject1, mobject2, + alpha, path_func = straight_path): """ Turns target_mobject into an interpolation between mobject1 and mobject2. diff --git a/triangle_of_power/__init__.py b/old_projects/brachistochrone/__init__.py similarity index 100% rename from triangle_of_power/__init__.py rename to old_projects/brachistochrone/__init__.py diff --git a/old_projects/brachistochrone/curves.py b/old_projects/brachistochrone/curves.py new file mode 100644 index 00000000..ac9f2f5a --- /dev/null +++ b/old_projects/brachistochrone/curves.py @@ -0,0 +1,719 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import * +from topics.characters import Randolph, Mathematician +from topics.functions import ParametricFunction, FunctionGraph +from topics.number_line import * +from mobject.region import Region, region_from_polygon_vertices +from scene import Scene + +RANDY_SCALE_VAL = 0.3 + + + +class Cycloid(ParametricFunction): + CONFIG = { + "point_a" : 6*LEFT+3*UP, + "radius" : 2, + "end_theta" : 3*np.pi/2, + "density" : 5*DEFAULT_POINT_DENSITY_1D, + "color" : YELLOW + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + ParametricFunction.__init__(self, self.pos_func, **kwargs) + + def pos_func(self, t): + T = t*self.end_theta + return self.point_a + self.radius * np.array([ + T - np.sin(T), + np.cos(T) - 1, + 0 + ]) + +class LoopTheLoop(ParametricFunction): + CONFIG = { + "color" : YELLOW_D, + "density" : 10*DEFAULT_POINT_DENSITY_1D + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + def func(t): + t = (6*np.pi/2)*(t-0.5) + return (t/2-np.sin(t))*RIGHT + \ + (np.cos(t)+(t**2)/10)*UP + ParametricFunction.__init__(self, func, **kwargs) + + +class SlideWordDownCycloid(Animation): + CONFIG = { + "rate_func" : None, + "run_time" : 8 + } + def __init__(self, word, **kwargs): + self.path = Cycloid(end_theta = np.pi) + word_mob = TextMobject(list(word)) + end_word = word_mob.copy() + end_word.shift(-end_word.get_bottom()) + end_word.shift(self.path.get_corner(DOWN+RIGHT)) + end_word.shift(3*RIGHT) + self.end_letters = end_word.split() + for letter in word_mob.split(): + letter.center() + letter.angle = 0 + unit_interval = np.arange(0, 1, 1./len(word)) + self.start_times = 0.5*(1-(unit_interval)) + Animation.__init__(self, word_mob, **kwargs) + + def update_mobject(self, alpha): + virtual_times = 2*(alpha - self.start_times) + cut_offs = [ + 0.1, + 0.3, + 0.7, + ] + for letter, time, end_letter in zip( + self.mobject.split(), virtual_times, self.end_letters + ): + time = max(time, 0) + time = min(time, 1) + if time < cut_offs[0]: + brightness = time/cut_offs[0] + letter.rgbs = brightness*np.ones(letter.rgbs.shape) + position = self.path.points[0] + angle = 0 + elif time < cut_offs[1]: + alpha = (time-cut_offs[0])/(cut_offs[1]-cut_offs[0]) + angle = -rush_into(alpha)*np.pi/2 + position = self.path.points[0] + elif time < cut_offs[2]: + alpha = (time-cut_offs[1])/(cut_offs[2]-cut_offs[1]) + index = int(alpha*self.path.get_num_points()) + position = self.path.points[index] + try: + angle = angle_of_vector( + self.path.points[index+1] - \ + self.path.points[index] + ) + except: + angle = letter.angle + else: + alpha = (time-cut_offs[2])/(1-cut_offs[2]) + start = self.path.points[-1] + end = end_letter.get_bottom() + position = interpolate(start, end, rush_from(alpha)) + angle = 0 + + letter.shift(position-letter.get_bottom()) + letter.rotate_in_place(angle-letter.angle) + letter.angle = angle + + +class BrachistochroneWordSliding(Scene): + def construct(self): + anim = SlideWordDownCycloid("Brachistochrone") + anim.path.gradient_highlight(WHITE, BLUE_E) + self.play(ShowCreation(anim.path)) + self.play(anim) + self.dither() + self.play( + FadeOut(anim.path), + ApplyMethod(anim.mobject.center) + ) + + + +class PathSlidingScene(Scene): + CONFIG = { + "gravity" : 3, + "delta_t" : 0.05, + "dither_and_add" : True, + "show_time" : True, + } + def slide(self, mobject, path, roll = False, ceiling = None): + points = path.points + time_slices = self.get_time_slices(points, ceiling = ceiling) + curr_t = 0 + last_index = 0 + curr_index = 1 + if self.show_time: + self.t_equals = TexMobject("t = ") + self.t_equals.shift(3.5*UP+4*RIGHT) + self.add(self.t_equals) + while curr_index < len(points): + self.slider = mobject.copy() + self.adjust_mobject_to_index( + self.slider, curr_index, points + ) + if roll: + distance = np.linalg.norm( + points[curr_index] - points[last_index] + ) + self.roll(mobject, distance) + self.add(self.slider) + if self.show_time: + self.write_time(curr_t) + self.dither(self.frame_duration) + self.remove(self.slider) + curr_t += self.delta_t + last_index = curr_index + while time_slices[curr_index] < curr_t: + curr_index += 1 + if curr_index == len(points): + break + if self.dither_and_add: + self.add(self.slider) + self.dither() + else: + return self.slider + + def get_time_slices(self, points, ceiling = None): + dt_list = np.zeros(len(points)) + ds_list = np.apply_along_axis( + np.linalg.norm, + 1, + points[1:]-points[:-1] + ) + if ceiling is None: + ceiling = points[0, 1] + delta_y_list = np.abs(ceiling - points[1:,1]) + delta_y_list += 0.001*(delta_y_list == 0) + v_list = self.gravity*np.sqrt(delta_y_list) + dt_list[1:] = ds_list / v_list + return np.cumsum(dt_list) + + def adjust_mobject_to_index(self, mobject, index, points): + point_a, point_b = points[index-1], points[index] + while np.all(point_a == point_b): + index += 1 + point_b = points[index] + theta = angle_of_vector(point_b - point_a) + mobject.rotate(theta) + mobject.shift(points[index]) + self.midslide_action(point_a, theta) + return mobject + + def midslide_action(self, point, angle): + pass + + def write_time(self, time): + if hasattr(self, "time_mob"): + self.remove(self.time_mob) + digits = map(TexMobject, "%.2f"%time) + digits[0].next_to(self.t_equals, buff = 0.1) + for left, right in zip(digits, digits[1:]): + right.next_to(left, buff = 0.1, aligned_edge = DOWN) + self.time_mob = Mobject(*digits) + self.add(self.time_mob) + + def roll(self, mobject, arc_length): + radius = mobject.get_width()/2 + theta = arc_length / radius + mobject.rotate_in_place(-theta) + + def add_cycloid_end_points(self): + cycloid = Cycloid() + point_a = Dot(cycloid.points[0]) + point_b = Dot(cycloid.points[-1]) + A = TexMobject("A").next_to(point_a, LEFT) + B = TexMobject("B").next_to(point_b, RIGHT) + self.add(point_a, point_b, A, B) + digest_locals(self) + + +class TryManyPaths(PathSlidingScene): + def construct(self): + randy = Randolph() + randy.shift(-randy.get_bottom()) + self.slider = randy.copy() + randy.scale(RANDY_SCALE_VAL) + paths = self.get_paths() + point_a = Dot(paths[0].points[0]) + point_b = Dot(paths[0].points[-1]) + A = TexMobject("A").next_to(point_a, LEFT) + B = TexMobject("B").next_to(point_b, RIGHT) + for point, tex in [(point_a, A), (point_b, B)]: + self.play(ShowCreation(point)) + self.play(ShimmerIn(tex)) + self.dither() + curr_path = None + for path in paths: + new_slider = self.adjust_mobject_to_index( + randy.copy(), 1, path.points + ) + if curr_path is None: + curr_path = path + self.play(ShowCreation(curr_path)) + else: + self.play(Transform(curr_path, path)) + self.play(Transform(self.slider, new_slider)) + self.dither() + self.remove(self.slider) + self.slide(randy, curr_path) + self.clear() + self.add(point_a, point_b, A, B, curr_path) + text = self.get_text() + text.to_edge(UP) + self.play(ShimmerIn(text)) + for path in paths: + self.play(Transform( + curr_path, path, + path_func = path_along_arc(np.pi/2), + run_time = 3 + )) + + def get_text(self): + return TextMobject("Which path is fastest?") + + def get_paths(self): + sharp_corner = Mobject( + Line(3*UP+LEFT, LEFT), + Arc(angle = np.pi/2, start_angle = np.pi), + Line(DOWN, DOWN+3*RIGHT) + ).ingest_submobjects().highlight(GREEN) + paths = [ + Arc( + angle = np.pi/2, + radius = 3, + start_angle = 4 + ), + LoopTheLoop(), + Line(7*LEFT, 7*RIGHT, color = RED_D), + sharp_corner, + FunctionGraph( + lambda x : 0.05*(x**2)+0.1*np.sin(2*x) + ), + FunctionGraph( + lambda x : x**2, + x_min = -3, + x_max = 2, + density = 3*DEFAULT_POINT_DENSITY_1D + ) + ] + cycloid = Cycloid() + self.align_paths(paths, cycloid) + return paths + [cycloid] + + def align_paths(self, paths, target_path): + start = target_path.points[0] + end = target_path.points[-1] + for path in paths: + path.position_endpoints_on(start, end) + + +class RollingRandolph(PathSlidingScene): + def construct(self): + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + self.add_cycloid_end_points() + self.slide(randy, self.cycloid, roll = True) + + + +class NotTheCircle(PathSlidingScene): + def construct(self): + self.add_cycloid_end_points() + start = self.point_a.get_center() + end = self.point_b.get_center() + angle = 2*np.pi/3 + path = Arc(angle, radius = 3) + path.gradient_highlight(RED_D, WHITE) + radius = Line(ORIGIN, path.points[0]) + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + randy_copy = randy.copy() + words = TextMobject("Circular paths are good, \\\\ but still not the best") + words.shift(UP) + + self.play( + ShowCreation(path), + ApplyMethod( + radius.rotate, + angle, + path_func = path_along_arc(angle) + ) + ) + self.play(FadeOut(radius)) + self.play( + ApplyMethod( + path.position_endpoints_on, start, end, + path_func = path_along_arc(-angle) + ), + run_time = 3 + ) + self.adjust_mobject_to_index(randy_copy, 1, path.points) + self.play(FadeIn(randy_copy)) + self.remove(randy_copy) + self.slide(randy, path) + self.play(ShimmerIn(words)) + self.dither() + + +class TransitionAwayFromSlide(PathSlidingScene): + def construct(self): + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + self.add_cycloid_end_points() + arrow = Arrow(ORIGIN, 2*RIGHT) + arrows = Mobject(*[ + arrow.copy().shift(vect) + for vect in 3*LEFT, ORIGIN, 3*RIGHT + ]) + arrows.shift(2*SPACE_WIDTH*RIGHT) + self.add(arrows) + + self.add(self.cycloid) + self.slide(randy, self.cycloid) + everything = Mobject(*self.mobjects) + self.play(ApplyMethod( + everything.shift, 4*SPACE_WIDTH*LEFT, + run_time = 2, + rate_func = rush_into + )) + + +class MinimalPotentialEnergy(Scene): + def construct(self): + horiz_radius = 5 + vert_radius = 3 + + vert_axis = NumberLine(numerical_radius = vert_radius) + vert_axis.rotate(np.pi/2) + vert_axis.shift(horiz_radius*LEFT) + horiz_axis = NumberLine( + numerical_radius = 5, + numbers_with_elongated_ticks = [] + ) + axes = Mobject(horiz_axis, vert_axis) + graph = FunctionGraph( + lambda x : 0.4*(x-2)*(x+2)+3, + x_min = -2, + x_max = 2, + density = 3*DEFAULT_POINT_DENSITY_1D + ) + graph.stretch_to_fit_width(2*horiz_radius) + graph.highlight(YELLOW) + min_point = Dot(graph.get_bottom()) + nature_finds = TextMobject("Nature finds this point") + nature_finds.scale(0.5) + nature_finds.highlight(GREEN) + nature_finds.shift(2*RIGHT+3*UP) + arrow = Arrow( + nature_finds.get_bottom(), min_point, + color = GREEN + ) + + side_words_start = TextMobject("Parameter describing") + top_words, last_side_words = [ + map(TextMobject, pair) + for pair in [ + ("Light's travel time", "Potential energy"), + ("path", "mechanical state") + ] + ] + for word in top_words + last_side_words + [side_words_start]: + word.scale(0.7) + side_words_start.next_to(horiz_axis, DOWN) + side_words_start.to_edge(RIGHT) + for words in top_words: + words.next_to(vert_axis, UP) + words.to_edge(LEFT) + for words in last_side_words: + words.next_to(side_words_start, DOWN) + for words in top_words[1], last_side_words[1]: + words.highlight(RED) + + self.add( + axes, top_words[0], side_words_start, + last_side_words[0] + ) + self.play(ShowCreation(graph)) + self.play( + ShimmerIn(nature_finds), + ShowCreation(arrow), + ShowCreation(min_point) + ) + self.dither() + self.play( + FadeOut(top_words[0]), + FadeOut(last_side_words[0]), + GrowFromCenter(top_words[1]), + GrowFromCenter(last_side_words[1]) + ) + self.dither(3) + + + + +class WhatGovernsSpeed(PathSlidingScene): + CONFIG = { + "num_pieces" : 6, + "dither_and_add" : False, + "show_time" : False, + } + def construct(self): + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + self.add_cycloid_end_points() + points = self.cycloid.points + ceiling = points[0, 1] + n = len(points) + broken_points = [ + points[k*n/self.num_pieces:(k+1)*n/self.num_pieces] + for k in range(self.num_pieces) + ] + words = TextMobject(""" + What determines the speed\\\\ + at each point? + """) + words.to_edge(UP) + + self.add(self.cycloid) + sliders, vectors = [], [] + for points in broken_points: + path = Mobject().add_points(points) + vect = points[-1] - points[-2] + magnitude = np.sqrt(ceiling - points[-1, 1]) + vect = magnitude*vect/np.linalg.norm(vect) + slider = self.slide(randy, path, ceiling = ceiling) + vector = Vector(slider.get_center(), vect) + self.add(slider, vector) + sliders.append(slider) + vectors.append(vector) + self.dither() + self.play(ShimmerIn(words)) + self.dither(3) + slider = sliders.pop(1) + vector = vectors.pop(1) + faders = sliders+vectors+[words] + self.play(*map(FadeOut, faders)) + self.remove(*faders) + self.show_geometry(slider, vector) + + def show_geometry(self, slider, vector): + point_a = self.point_a.get_center() + horiz_line = Line(point_a, point_a + 6*RIGHT) + ceil_point = point_a + ceil_point[0] = slider.get_center()[0] + vert_brace = Brace( + Mobject(Point(ceil_point), Point(slider.get_center())), + RIGHT, + buff = 0.5 + ) + vect_brace = Brace(slider) + vect_brace.stretch_to_fit_width(vector.get_length()) + vect_brace.rotate(np.arctan(vector.get_slope())) + vect_brace.center().shift(vector.get_center()) + nudge = 0.2*(DOWN+LEFT) + vect_brace.shift(nudge) + y_mob = TexMobject("y") + y_mob.next_to(vert_brace) + sqrt_y = TexMobject("k\\sqrt{y}") + sqrt_y.scale(0.5) + sqrt_y.shift(vect_brace.get_center()) + sqrt_y.shift(3*nudge) + + self.play(ShowCreation(horiz_line)) + self.play( + GrowFromCenter(vert_brace), + ShimmerIn(y_mob) + ) + self.play( + GrowFromCenter(vect_brace), + ShimmerIn(sqrt_y) + ) + self.dither(3) + self.solve_energy() + + def solve_energy(self): + loss_in_potential = TextMobject("Loss in potential: ") + loss_in_potential.shift(2*UP) + potential = TexMobject("m g y".split()) + potential.next_to(loss_in_potential) + kinetic = TexMobject([ + "\\dfrac{1}{2}","m","v","^2","=" + ]) + kinetic.next_to(potential, LEFT) + nudge = 0.1*UP + kinetic.shift(nudge) + loss_in_potential.shift(nudge) + ms = Mobject(kinetic.split()[1], potential.split()[0]) + two = TexMobject("2") + two.shift(ms.split()[1].get_center()) + half = kinetic.split()[0] + sqrt = TexMobject("\\sqrt{\\phantom{2mg}}") + sqrt.shift(potential.get_center()) + nudge = 0.2*LEFT + sqrt.shift(nudge) + squared = kinetic.split()[3] + equals = kinetic.split()[-1] + new_eq = equals.copy().next_to(kinetic.split()[2]) + + self.play( + Transform( + Point(loss_in_potential.get_left()), + loss_in_potential + ), + *map(GrowFromCenter, potential.split()) + ) + self.dither(2) + self.play( + FadeOut(loss_in_potential), + GrowFromCenter(kinetic) + ) + self.dither(2) + self.play(ApplyMethod(ms.shift, 5*UP)) + self.dither() + self.play(Transform( + half, two, + path_func = counterclockwise_path() + )) + self.dither() + self.play( + Transform( + squared, sqrt, + path_func = clockwise_path() + ), + Transform(equals, new_eq) + ) + self.dither(2) + + + + +class ThetaTInsteadOfXY(Scene): + def construct(self): + cycloid = Cycloid() + index = cycloid.get_num_points()/3 + point = cycloid.points[index] + vect = cycloid.points[index+1]-point + vect /= np.linalg.norm(vect) + vect *= 3 + vect_mob = Vector(point, vect) + dot = Dot(point) + xy = TexMobject("\\big( x(t), y(t) \\big)") + xy.next_to(dot, UP+RIGHT, buff = 0.1) + vert_line = Line(2*DOWN, 2*UP) + vert_line.shift(point) + angle = vect_mob.get_angle() + np.pi/2 + arc = Arc(angle, radius = 1, start_angle = -np.pi/2) + arc.shift(point) + theta = TexMobject("\\theta(t)") + theta.next_to(arc, DOWN, buff = 0.1, aligned_edge = LEFT) + theta.shift(0.2*RIGHT) + + self.play(ShowCreation(cycloid)) + self.play(ShowCreation(dot)) + self.play(ShimmerIn(xy)) + self.dither() + self.play( + FadeOut(xy), + ShowCreation(vect_mob) + ) + self.play( + ShowCreation(arc), + ShowCreation(vert_line), + ShimmerIn(theta) + ) + self.dither() + + +class DefineCurveWithKnob(PathSlidingScene): + def construct(self): + self.knob = Circle(color = BLUE_D) + self.knob.add_line(UP, DOWN) + self.knob.to_corner(UP+RIGHT) + self.knob.shift(0.5*DOWN) + self.last_angle = np.pi/2 + arrow = Vector(ORIGIN, RIGHT) + arrow.next_to(self.knob, LEFT) + words = TextMobject("Turn this knob over time to define the curve") + words.next_to(arrow, LEFT) + self.path = self.get_path() + self.path.shift(1.5*DOWN) + self.path.show() + self.path.highlight(BLACK) + + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + + self.play(ShimmerIn(words)) + self.play(ShowCreation(arrow)) + self.play(ShowCreation(self.knob)) + self.dither() + self.add(self.path) + + self.slide(randy, self.path) + self.dither() + + + def get_path(self): + return Cycloid(end_theta = 2*np.pi) + + def midslide_action(self, point, angle): + d_angle = angle-self.last_angle + self.knob.rotate_in_place(d_angle) + self.last_angle = angle + self.path.highlight(BLUE_D, lambda p : p[0] < point[0]) + + + +class WonkyDefineCurveWithKnob(DefineCurveWithKnob): + def get_path(self): + return ParametricFunction( + lambda t : t*RIGHT + (-0.2*t-np.sin(2*np.pi*t/6))*UP, + start = -7, + end = 10 + ) + + +class SlowDefineCurveWithKnob(DefineCurveWithKnob): + def get_path(self): + return ParametricFunction( + lambda t : t*RIGHT + (np.exp(-(t+2)**2)-0.2*np.exp(t-2)), + start = -4, + end = 4 + ) + + +class BumpyDefineCurveWithKnob(DefineCurveWithKnob): + def get_path(self): + + result = FunctionGraph( + lambda x : 0.05*(x**2)+0.1*np.sin(2*x) + ) + result.rotate(-np.pi/20) + result.scale(0.7) + result.shift(DOWN) + return result + + + + + + + + + + + + + diff --git a/old_projects/brachistochrone/cycloid.py b/old_projects/brachistochrone/cycloid.py new file mode 100644 index 00000000..f0fb4b51 --- /dev/null +++ b/old_projects/brachistochrone/cycloid.py @@ -0,0 +1,611 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject, Mobject1D +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from topics.characters import Randolph +from topics.functions import * +from mobject.region import Region +from scene import Scene +from scene.zoomed_scene import ZoomedScene + +from camera import Camera +from brachistochrone.curves import * + +class RollAlongVector(Animation): + CONFIG = { + "rotation_vector" : OUT, + } + def __init__(self, mobject, vector, **kwargs): + radius = mobject.get_width()/2 + radians = np.linalg.norm(vector)/radius + last_alpha = 0 + digest_config(self, kwargs, locals()) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + d_alpha = alpha - self.last_alpha + self.last_alpha = alpha + self.mobject.rotate_in_place( + d_alpha*self.radians, + self.rotation_vector + ) + self.mobject.shift(d_alpha*self.vector) + + +class CycloidScene(Scene): + CONFIG = { + "point_a" : 6*LEFT+3*UP, + "radius" : 2, + "end_theta" : 2*np.pi + } + def construct(self): + self.generate_cycloid() + self.generate_circle() + self.generate_ceiling() + + def grow_parts(self): + self.play(*[ + ShowCreation(mob) + for mob in self.circle, self.ceiling + ]) + + def generate_cycloid(self): + self.cycloid = Cycloid( + point_a = self.point_a, + radius = self.radius, + end_theta = self.end_theta + ) + + def generate_circle(self, **kwargs): + self.circle = Circle(radius = self.radius, **kwargs) + self.circle.shift(self.point_a - self.circle.get_top()) + radial_line = Line( + self.circle.get_center(), self.point_a + ) + self.circle.add(radial_line) + + def generate_ceiling(self): + self.ceiling = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) + self.ceiling.shift(self.cycloid.get_top()[1]*UP) + + def draw_cycloid(self, run_time = 3, *anims, **kwargs): + kwargs["run_time"] = run_time + self.play( + RollAlongVector( + self.circle, + self.cycloid.points[-1]-self.cycloid.points[0], + **kwargs + ), + ShowCreation(self.cycloid, **kwargs), + *anims + ) + + def roll_back(self, run_time = 3, *anims, **kwargs): + kwargs["run_time"] = run_time + self.play( + RollAlongVector( + self.circle, + self.cycloid.points[0]-self.cycloid.points[- 1], + rotation_vector = IN, + **kwargs + ), + ShowCreation( + self.cycloid, + rate_func = lambda t : smooth(1-t), + **kwargs + ), + *anims + ) + self.generate_cycloid() + + +class IntroduceCycloid(CycloidScene): + def construct(self): + CycloidScene.construct(self) + + equation = TexMobject([ + "\\dfrac{\\sin(\\theta)}{\\sqrt{y}}", + "= \\text{constant}" + ]) + sin_sqrt, const = equation.split() + new_eq = equation.copy() + new_eq.to_edge(UP, buff = 1.3) + cycloid_word = TextMobject("Cycloid") + arrow = Arrow(2*UP, cycloid_word) + arrow.reverse_points() + q_mark = TextMobject("?") + + self.play(*map(ShimmerIn, equation.split())) + self.dither() + self.play( + ApplyMethod(equation.shift, 2.2*UP), + ShowCreation(arrow) + ) + q_mark.next_to(sin_sqrt) + self.play(ShimmerIn(cycloid_word)) + self.dither() + self.grow_parts() + self.draw_cycloid() + self.dither() + extra_terms = [const, arrow, cycloid_word] + self.play(*[ + Transform(mob, q_mark) + for mob in extra_terms + ]) + self.remove(*extra_terms) + self.roll_back() + q_marks, arrows = self.get_q_marks_and_arrows(sin_sqrt) + self.draw_cycloid(3, + ShowCreation(q_marks), + ShowCreation(arrows) + ) + self.dither() + + def get_q_marks_and_arrows(self, mob, n_marks = 10): + circle = Circle().replace(mob) + q_marks, arrows = result = [Mobject(), Mobject()] + for x in range(n_marks): + index = (x+0.5)*self.cycloid.get_num_points()/n_marks + q_point = self.cycloid.points[index] + vect = q_point-mob.get_center() + start_point = circle.get_boundary_point(vect) + arrow = Arrow( + start_point, q_point, + color = BLUE_E + ) + + q_marks.add(TextMobject("?").shift(q_point)) + arrows.add(arrow) + for mob in result: + mob.ingest_submobjects() + return result + + +class LeviSolution(CycloidScene): + CONFIG = { + "cycloid_fraction" : 0.25, + } + def construct(self): + CycloidScene.construct(self) + self.add(self.ceiling) + self.generate_points() + methods = [ + self.draw_cycloid, + self.roll_into_position, + self.draw_p_and_c, + self.show_pendulum, + self.show_diameter, + self.show_theta, + self.show_similar_triangles, + self.show_sin_thetas, + self.show_y, + self.rearrange, + ] + for method in methods: + method() + self.dither() + + + def generate_points(self): + index = int(self.cycloid_fraction*self.cycloid.get_num_points()) + p_point = self.cycloid.points[index] + p_dot = Dot(p_point) + p_label = TexMobject("P") + p_label.next_to(p_dot, DOWN+LEFT) + c_point = self.point_a + self.cycloid_fraction*self.radius*2*np.pi*RIGHT + c_dot = Dot(c_point) + c_label = TexMobject("C") + c_label.next_to(c_dot, UP) + + digest_locals(self) + + def roll_into_position(self): + self.play(RollAlongVector( + self.circle, + (1-self.cycloid_fraction)*self.radius*2*np.pi*LEFT, + rotation_vector = IN, + run_time = 2 + )) + + def draw_p_and_c(self): + radial_line = self.circle.submobjects[0] ##Hacky + self.play(Transform(radial_line, self.p_dot)) + self.remove(radial_line) + self.add(self.p_dot) + self.play(ShimmerIn(self.p_label)) + self.dither() + self.play(Transform(self.ceiling.copy(), self.c_dot)) + self.play(ShimmerIn(self.c_label)) + + def show_pendulum(self, arc_angle = np.pi, arc_color = GREEN): + words = TextMobject(": Instantaneous center of rotation") + words.next_to(self.c_label) + line = Line(self.p_point, self.c_point) + line_angle = line.get_angle()+np.pi + line_length = line.get_length() + line.add(self.p_dot.copy()) + line.get_center = lambda : self.c_point + tangent_line = Line(3*LEFT, 3*RIGHT) + tangent_line.rotate(line_angle-np.pi/2) + tangent_line.shift(self.p_point) + tangent_line.highlight(arc_color) + right_angle_symbol = Mobject( + Line(UP, UP+RIGHT), + Line(UP+RIGHT, RIGHT) + ) + right_angle_symbol.scale(0.3) + right_angle_symbol.rotate(tangent_line.get_angle()+np.pi) + right_angle_symbol.shift(self.p_point) + + self.play(ShowCreation(line)) + self.play(ShimmerIn(words)) + self.dither() + pairs = [ + (line_angle, arc_angle/2), + (line_angle+arc_angle/2, -arc_angle), + (line_angle-arc_angle/2, arc_angle/2), + ] + arcs = [] + for start, angle in pairs: + arc = Arc( + angle = angle, + radius = line_length, + start_angle = start, + color = GREEN + ) + arc.shift(self.c_point) + self.play( + ShowCreation(arc), + ApplyMethod( + line.rotate_in_place, + angle, + path_func = path_along_arc(angle) + ), + run_time = 2 + ) + arcs.append(arc) + self.dither() + self.play(Transform(arcs[1], tangent_line)) + self.add(tangent_line) + self.play(ShowCreation(right_angle_symbol)) + self.dither() + + self.tangent_line = tangent_line + self.right_angle_symbol = right_angle_symbol + self.pc_line = line + self.remove(words, *arcs) + + def show_diameter(self): + exceptions = [ + self.circle, + self.tangent_line, + self.pc_line, + self.right_angle_symbol + ] + everything = set(self.mobjects).difference(exceptions) + everything_copy = Mobject(*everything).copy() + light_everything = everything_copy.copy() + dark_everything = everything_copy.copy() + dark_everything.fade(0.8) + bottom_point = np.array(self.c_point) + bottom_point += 2*self.radius*DOWN + diameter = Line(bottom_point, self.c_point) + brace = Brace(diameter, RIGHT) + diameter_word = TextMobject("Diameter") + d_mob = TexMobject("D") + diameter_word.next_to(brace) + d_mob.next_to(diameter) + + self.remove(*everything) + self.play(Transform(everything_copy, dark_everything)) + self.dither() + self.play(ShowCreation(diameter)) + self.play(GrowFromCenter(brace)) + self.play(ShimmerIn(diameter_word)) + self.dither() + self.play(*[ + Transform(mob, d_mob) + for mob in brace, diameter_word + ]) + self.remove(brace, diameter_word) + self.add(d_mob) + self.play(Transform(everything_copy, light_everything)) + self.remove(everything_copy) + self.add(*everything) + + self.d_mob = d_mob + self.bottom_point = bottom_point + + def show_theta(self, radius = 1): + arc = Arc( + angle = self.tangent_line.get_angle()-np.pi/2, + radius = radius, + start_angle = np.pi/2 + ) + + theta = TexMobject("\\theta") + theta.shift(1.5*arc.get_center()) + Mobject(arc, theta).shift(self.bottom_point) + + self.play( + ShowCreation(arc), + ShimmerIn(theta) + ) + self.arc = arc + self.theta = theta + + def show_similar_triangles(self): + y_point = np.array(self.p_point) + y_point[1] = self.point_a[1] + new_arc = Arc( + angle = self.tangent_line.get_angle()-np.pi/2, + radius = 0.5, + start_angle = np.pi + ) + new_arc.shift(self.c_point) + new_theta = self.theta.copy() + new_theta.next_to(new_arc, LEFT) + new_theta.shift(0.1*DOWN) + kwargs = { + "stroke_width" : 2*DEFAULT_POINT_THICKNESS, + } + triangle1 = Polygon( + self.p_point, self.c_point, self.bottom_point, + color = MAROON, + **kwargs + ) + triangle2 = Polygon( + y_point, self.p_point, self.c_point, + color = WHITE, + **kwargs + ) + y_line = Line(self.p_point, y_point) + + self.play( + Transform(self.arc.copy(), new_arc), + Transform(self.theta.copy(), new_theta), + run_time = 3 + ) + self.dither() + self.play(FadeIn(triangle1)) + self.dither() + self.play(Transform(triangle1, triangle2)) + self.play(ApplyMethod(triangle1.highlight, MAROON)) + self.dither() + self.remove(triangle1) + self.add(y_line) + + self.y_line = y_line + + def show_sin_thetas(self): + pc = Line(self.p_point, self.c_point) + mob = Mobject(self.theta, self.d_mob).copy() + mob.ingest_submobjects() + triplets = [ + (pc, "D\\sin(\\theta)", 0.5), + (self.y_line, "D\\sin^2(\\theta)", 0.7), + ] + for line, tex, scale in triplets: + trig_mob = TexMobject(tex) + trig_mob.scale_to_fit_width( + scale*line.get_length() + ) + trig_mob.shift(-1.2*trig_mob.get_top()) + trig_mob.rotate(line.get_angle()) + trig_mob.shift(line.get_center()) + if line is self.y_line: + trig_mob.shift(0.1*UP) + + self.play(Transform(mob, trig_mob)) + self.add(trig_mob) + self.dither() + + self.remove(mob) + self.d_sin_squared_theta = trig_mob + + + def show_y(self): + y_equals = TexMobject(["y", "="]) + y_equals.shift(2*UP) + y_expression = TexMobject([ + "D ", "\\sin", "^2", "(\\theta)" + ]) + y_expression.next_to(y_equals) + y_expression.shift(0.05*UP+0.1*RIGHT) + temp_expr = self.d_sin_squared_theta.copy() + temp_expr.rotate(-np.pi/2) + temp_expr.replace(y_expression) + y_mob = TexMobject("y") + y_mob.next_to(self.y_line, RIGHT) + y_mob.shift(0.2*UP) + + self.play( + Transform(self.d_sin_squared_theta, temp_expr), + ShimmerIn(y_mob), + ShowCreation(y_equals) + ) + self.remove(self.d_sin_squared_theta) + self.add(y_expression) + + self.y_equals = y_equals + self.y_expression = y_expression + + def rearrange(self): + sqrt_nudge = 0.2*LEFT + y, equals = self.y_equals.split() + d, sin, squared, theta = self.y_expression.split() + y_sqrt = TexMobject("\\sqrt{\\phantom{y}}") + d_sqrt = y_sqrt.copy() + y_sqrt.shift(y.get_center()+sqrt_nudge) + d_sqrt.shift(d.get_center()+sqrt_nudge) + + self.play( + ShimmerIn(y_sqrt), + ShimmerIn(d_sqrt), + ApplyMethod(squared.shift, 4*UP), + ApplyMethod(theta.shift, 1.5* squared.get_width()*LEFT) + ) + self.dither() + y_sqrt.add(y) + d_sqrt.add(d) + sin.add(theta) + + sin_over = TexMobject("\\dfrac{\\phantom{\\sin(\\theta)}}{\\quad}") + sin_over.next_to(sin, DOWN, 0.15) + new_eq = equals.copy() + new_eq.next_to(sin_over, LEFT) + one_over = TexMobject("\\dfrac{1}{\\quad}") + one_over.next_to(new_eq, LEFT) + one_over.shift( + (sin_over.get_bottom()[1]-one_over.get_bottom()[1])*UP + ) + + self.play( + Transform(equals, new_eq), + ShimmerIn(sin_over), + ShimmerIn(one_over), + ApplyMethod( + d_sqrt.next_to, one_over, DOWN, + path_func = path_along_arc(-np.pi) + ), + ApplyMethod( + y_sqrt.next_to, sin_over, DOWN, + path_func = path_along_arc(-np.pi) + ), + run_time = 2 + ) + self.dither() + + brace = Brace(d_sqrt, DOWN) + constant = TextMobject("Constant") + constant.next_to(brace, DOWN) + + self.play( + GrowFromCenter(brace), + ShimmerIn(constant) + ) + + + + +class EquationsForCycloid(CycloidScene): + def construct(self): + CycloidScene.construct(self) + equations = TexMobject([ + "x(t) = Rt - R\\sin(t)", + "y(t) = -R + R\\cos(t)" + ]) + top, bottom = equations.split() + bottom.next_to(top, DOWN) + equations.center() + equations.to_edge(UP, buff = 1.3) + + self.play(ShimmerIn(equations)) + self.grow_parts() + self.draw_cycloid(rate_func = None, run_time = 5) + self.dither() + +class SlidingObject(CycloidScene, PathSlidingScene): + CONFIG = { + "show_time" : False, + "dither_and_add" : False + } + + args_list = [(True,), (False,)] + + @staticmethod + def args_to_string(with_words): + return "WithWords" if with_words else "WithoutWords" + + @staticmethod + def string_to_args(string): + return string == "WithWords" + + def construct(self, with_words): + CycloidScene.construct(self) + + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + central_randy = randy.copy() + start_randy = self.adjust_mobject_to_index( + randy.copy(), 1, self.cycloid.points + ) + + if with_words: + words1 = TextMobject("Trajectory due to gravity") + arrow = TexMobject("\\leftrightarrow") + words2 = TextMobject("Trajectory due \\emph{constantly} rotating wheel") + words1.next_to(arrow, LEFT) + words2.next_to(arrow, RIGHT) + words = Mobject(words1, arrow, words2) + words.scale_to_fit_width(2*SPACE_WIDTH-1) + words.to_edge(UP, buff = 0.2) + words.to_edge(LEFT) + + self.play(ShowCreation(self.cycloid.copy())) + self.slide(randy, self.cycloid) + self.add(self.slider) + self.dither() + self.grow_parts() + self.draw_cycloid() + self.dither() + self.play(Transform(self.slider, start_randy)) + self.dither() + self.roll_back() + self.dither() + if with_words: + self.play(*map(ShimmerIn, [words1, arrow, words2])) + self.dither() + self.remove(self.circle) + start_time = len(self.frames)*self.frame_duration + self.remove(self.slider) + self.slide(central_randy, self.cycloid) + end_time = len(self.frames)*self.frame_duration + self.play_over_time_range( + start_time, + end_time, + RollAlongVector( + self.circle, + self.cycloid.points[-1]-self.cycloid.points[0], + run_time = end_time-start_time, + rate_func = None + ) + ) + self.add(self.circle, self.slider) + self.dither() + + + +class RotateWheel(CycloidScene): + def construct(self): + CycloidScene.construct(self) + self.circle.center() + + self.play(Rotating( + self.circle, + axis = OUT, + run_time = 5, + rate_func = smooth + )) + + + + + + + + + + diff --git a/old_projects/brachistochrone/drawing_images.py b/old_projects/brachistochrone/drawing_images.py new file mode 100644 index 00000000..3d66edd9 --- /dev/null +++ b/old_projects/brachistochrone/drawing_images.py @@ -0,0 +1,429 @@ +import numpy as np +import itertools as it +import operator as op +import sys +import inspect +from PIL import Image +import cv2 +import random +from scipy.spatial.distance import cdist +from scipy import ndimage + +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from mobject.tex_mobject import TextMobject, TexMobject + +from animation.transform import \ + Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\ + FadeIn, FadeOut, GrowFromCenter, ShimmerIn, ApplyMethod +from animation.simple_animations import \ + ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import \ + Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point +from topics.characters import Randolph, Mathematician, ThoughtBubble +from topics.functions import ParametricFunction +from topics.number_line import NumberPlane +from mobject.region import Region, region_from_polygon_vertices +from scene import Scene + + +DEFAULT_GAUSS_BLUR_CONFIG = { + "ksize" : (5, 5), + "sigmaX" : 6, + "sigmaY" : 6, +} + +DEFAULT_CANNY_CONFIG = { + "threshold1" : 50, + "threshold2" : 100, +} + +DEFAULT_BLUR_RADIUS = 0.5 +DEFAULT_CONNECTED_COMPONENT_THRESHOLD = 25 + + +def reverse_colors(nparray): + return nparray[:,:,[2, 1, 0]] + +def show(nparray): + Image.fromarray(reverse_colors(nparray)).show() + + +def thicken(nparray): + height, width = nparray.shape + nparray = nparray.reshape((height, width, 1)) + return np.repeat(nparray, 3, 2) + +def sort_by_color(mob): + indices = np.argsort(np.apply_along_axis( + lambda p : -np.linalg.norm(p), + 1, + mob.rgbs + )) + mob.rgbs = mob.rgbs[indices] + mob.points = mob.points[indices] + + + +def get_image_array(name): + image_files = os.listdir(IMAGE_DIR) + possibilities = filter(lambda s : s.startswith(name), image_files) + for possibility in possibilities: + try: + path = os.path.join(IMAGE_DIR, possibility) + image = Image.open(path) + image = image.convert('RGB') + return np.array(image) + except: + pass + raise Exception("Image for %s not found"%name) + +def get_edges(image_array): + blurred = cv2.GaussianBlur( + image_array, + **DEFAULT_GAUSS_BLUR_CONFIG + ) + edges = cv2.Canny( + blurred, + **DEFAULT_CANNY_CONFIG + ) + return edges + +def nearest_neighbor_align(mobject1, mobject2): + distance_matrix = cdist(mobject1.points, mobject2.points) + closest_point_indices = np.apply_along_axis( + np.argmin, 0, distance_matrix + ) + new_mob1 = Mobject() + new_mob2 = Mobject() + for n in range(mobject1.get_num_points()): + indices = (closest_point_indices == n) + new_mob1.add_points( + [mobject1.points[n]]*sum(indices) + ) + new_mob2.add_points( + mobject2.points[indices], + rgbs = mobject2.rgbs[indices] + ) + return new_mob1, new_mob2 + +def get_connected_components(image_array, + blur_radius = DEFAULT_BLUR_RADIUS, + threshold = DEFAULT_CONNECTED_COMPONENT_THRESHOLD): + blurred_image = ndimage.gaussian_filter(image_array, blur_radius) + labels, component_count = ndimage.label(blurred_image > threshold) + return [ + image_array * (labels == count) + for count in range(1, component_count+1) + ] + +def color_region(bw_region, colored_image): + return thicken(bw_region > 0) * colored_image + + +class TracePicture(Scene): + args_list = [ + ("Newton",), + ("Mark_Levi",), + ("Steven_Strogatz",), + ("Pierre_de_Fermat",), + ("Galileo_Galilei",), + ("Jacob_Bernoulli",), + ("Johann_Bernoulli2",), + ("Old_Newton",) + ] + + @staticmethod + def args_to_string(name): + return name + + @staticmethod + def string_to_args(name): + return name + + def construct(self, name): + run_time = 20 + scale_factor = 0.8 + image_array = get_image_array(name) + edge_mobject = self.get_edge_mobject(image_array) + full_picture = MobjectFromPixelArray(image_array) + for mob in edge_mobject, full_picture: + # mob.stroke_width = 4 + mob.scale(scale_factor) + mob.show() + + self.play( + DelayByOrder(FadeIn( + full_picture, + run_time = run_time, + rate_func = squish_rate_func(smooth, 0.7, 1) + )), + ShowCreation( + edge_mobject, + run_time = run_time, + rate_func = None + ) + ) + self.remove(edge_mobject) + self.dither() + + + def get_edge_mobject(self, image_array): + edged_image = get_edges(image_array) + individual_edges = get_connected_components(edged_image) + colored_edges = [ + color_region(edge, image_array) + for edge in individual_edges + ] + colored_edge_mobject_list = [ + MobjectFromPixelArray(colored_edge) + for colored_edge in colored_edges + ] + random.shuffle(colored_edge_mobject_list) + edge_mobject = Mobject(*colored_edge_mobject_list) + edge_mobject.ingest_submobjects() + return edge_mobject + + + +class JohannThinksHeIsBetter(Scene): + def construct(self): + names = [ + "Johann_Bernoulli2", + "Jacob_Bernoulli", + "Gottfried_Wilhelm_von_Leibniz", + "Newton" + ] + guys = [ + ImageMobject(name, invert = False) + for name in names + ] + johann = guys[0] + johann.scale(0.8) + pensive_johann = johann.copy() + pensive_johann.scale(0.25) + pensive_johann.to_corner(DOWN+LEFT) + comparitive_johann = johann.copy() + template = Square(side_length = 2) + comparitive_johann.replace(template) + comparitive_johann.shift(UP+LEFT) + greater_than = TexMobject(">") + greater_than.next_to(comparitive_johann) + for guy, name in zip(guys, names)[1:]: + guy.replace(template) + guy.next_to(greater_than) + name_mob = TextMobject(name.replace("_", " ")) + name_mob.scale(0.5) + name_mob.next_to(guy, DOWN) + guy.name_mob = name_mob + guy.sort_points(lambda p : np.dot(p, DOWN+RIGHT)) + bubble = ThoughtBubble(initial_width = 12) + bubble.stretch_to_fit_height(6) + bubble.ingest_submobjects() + bubble.pin_to(pensive_johann) + bubble.shift(DOWN) + point = Point(johann.get_corner(UP+RIGHT)) + upper_point = Point(comparitive_johann.get_corner(UP+RIGHT)) + lightbulb = ImageMobject("Lightbulb", invert = False) + lightbulb.scale(0.1) + lightbulb.sort_points(np.linalg.norm) + lightbulb.next_to(upper_point, RIGHT) + + self.add(johann) + self.dither() + self.play( + Transform(johann, pensive_johann), + Transform(point, bubble), + run_time = 2 + ) + self.remove(point) + self.add(bubble) + weakling = guys[1] + self.play( + FadeIn(comparitive_johann), + ShowCreation(greater_than), + FadeIn(weakling) + ) + self.dither(2) + for guy in guys[2:]: + self.play(DelayByOrder(Transform( + weakling, upper_point + ))) + self.play( + FadeIn(guy), + ShimmerIn(guy.name_mob) + ) + self.dither(3) + self.remove(guy.name_mob) + weakling = guy + self.play(FadeOut(weakling), FadeOut(greater_than)) + self.play(ShowCreation(lightbulb)) + self.dither() + self.play(FadeOut(comparitive_johann), FadeOut(lightbulb)) + self.play(ApplyMethod( + Mobject(johann, bubble).scale, 10, + run_time = 3 + )) + + +class NewtonVsJohann(Scene): + def construct(self): + newton, johann = [ + ImageMobject(name, invert = False).scale(0.5) + for name in "Newton", "Johann_Bernoulli2" + ] + greater_than = TexMobject(">") + newton.next_to(greater_than, RIGHT) + johann.next_to(greater_than, LEFT) + self.add(johann, greater_than, newton) + for i in range(2): + kwargs = { + "path_func" : counterclockwise_path(), + "run_time" : 2 + } + self.play( + ApplyMethod(newton.replace, johann, **kwargs), + ApplyMethod(johann.replace, newton, **kwargs), + ) + self.dither() + + +class JohannThinksOfFermat(Scene): + def construct(self): + johann, fermat = [ + ImageMobject(name, invert = False) + for name in "Johann_Bernoulli2", "Pierre_de_Fermat" + ] + johann.scale(0.2) + johann.to_corner(DOWN+LEFT) + bubble = ThoughtBubble(initial_width = 12) + bubble.stretch_to_fit_height(6) + bubble.pin_to(johann) + bubble.shift(DOWN) + bubble.add_content(fermat) + fermat.scale_in_place(0.4) + + + self.add(johann, bubble) + self.dither() + self.play(FadeIn(fermat)) + self.dither() + + +class MathematiciansOfEurope(Scene): + def construct(self): + europe = ImageMobject("Europe", use_cache = False) + self.add(europe) + self.freeze_background() + + mathematicians = [ + ("Newton", [-1.75, -0.75, 0]), + ("Jacob_Bernoulli",[-0.75, -1.75, 0]), + ("Ehrenfried_von_Tschirnhaus",[0.5, -0.5, 0]), + ("Gottfried_Wilhelm_von_Leibniz",[0.2, -1.75, 0]), + ("Guillaume_de_L'Hopital", [-1.75, -1.25, 0]), + ] + + for name, point in mathematicians: + man = ImageMobject(name, invert = False) + if name == "Newton": + name = "Isaac_Newton" + name_mob = TextMobject(name.replace("_", " ")) + name_mob.to_corner(UP+LEFT, buff=0.75) + self.add(name_mob) + man.scale_to_fit_height(4) + mobject = Point(man.get_corner(UP+LEFT)) + self.play(Transform(mobject, man)) + man.scale(0.2) + man.shift(point) + self.play(Transform(mobject, man)) + self.remove(name_mob) + +class OldNewtonIsDispleased(Scene): + def construct(self): + old_newton = ImageMobject("Old_Newton", invert = False) + old_newton.scale(0.8) + self.add(old_newton) + self.freeze_background() + + words = TextMobject("Note the displeasure") + words.to_corner(UP+RIGHT) + face_point = 1.8*UP+0.5*LEFT + arrow = Arrow(words.get_bottom(), face_point) + + + self.play(ShimmerIn(words)) + self.play(ShowCreation(arrow)) + self.dither() + + +class NewtonConsideredEveryoneBeneathHim(Scene): + def construct(self): + mathematicians = [ + ImageMobject(name, invert = False) + for name in [ + "Old_Newton", + "Johann_Bernoulli2", + "Jacob_Bernoulli", + "Ehrenfried_von_Tschirnhaus", + "Gottfried_Wilhelm_von_Leibniz", + "Guillaume_de_L'Hopital", + ] + ] + newton = mathematicians.pop(0) + newton.scale(0.8) + new_newton = newton.copy() + new_newton.scale_to_fit_height(3) + new_newton.to_edge(UP) + for man in mathematicians: + man.scale_to_fit_width(1.7) + johann = mathematicians.pop(0) + johann.next_to(new_newton, DOWN) + last_left, last_right = johann, johann + for man, count in zip(mathematicians, it.count()): + if count%2 == 0: + man.next_to(last_left, LEFT) + last_left = man + else: + man.next_to(last_right, RIGHT) + last_right = man + + self.play( + Transform(newton, new_newton), + GrowFromCenter(johann) + ) + self.dither() + self.play(FadeIn(Mobject(*mathematicians))) + self.dither() + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/old_projects/brachistochrone/graveyard.py b/old_projects/brachistochrone/graveyard.py new file mode 100644 index 00000000..97cd9143 --- /dev/null +++ b/old_projects/brachistochrone/graveyard.py @@ -0,0 +1,344 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject, Mobject1D +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import * +from topics.characters import Randolph, Mathematician +from topics.functions import * +from topics.number_line import * +from mobject.region import Region, region_from_polygon_vertices +from scene import Scene +from scene.zoomed_scene import ZoomedScene + +from brachistochrone.curves import Cycloid + +class MultilayeredGlass(PhotonScene, ZoomedScene): + CONFIG = { + "num_discrete_layers" : 5, + "num_variables" : 3, + "top_color" : BLUE_E, + "bottom_color" : BLUE_A, + "zoomed_canvas_space_shape" : (5, 5), + "square_color" : GREEN_B, + } + def construct(self): + self.cycloid = Cycloid(end_theta = np.pi) + self.cycloid.highlight(YELLOW) + self.top = self.cycloid.get_top()[1] + self.bottom = self.cycloid.get_bottom()[1]-1 + self.generate_layers() + self.generate_discrete_path() + photon_run = self.photon_run_along_path( + self.discrete_path, + run_time = 1, + rate_func = rush_into + ) + + + + self.continuous_to_smooth() + self.add(*self.layers) + self.show_layer_variables() + self.play(photon_run) + self.play(ShowCreation(self.discrete_path)) + self.isolate_bend_points() + self.clear() + self.add(*self.layers) + self.show_main_equation() + self.ask_continuous_question() + + def continuous_to_smooth(self): + self.add(*self.layers) + continuous = self.get_continuous_background() + self.add(continuous) + self.dither() + self.play(ShowCreation( + continuous, + rate_func = lambda t : smooth(1-t) + )) + self.remove(continuous) + self.dither() + + def get_continuous_background(self): + glass = FilledRectangle( + height = self.top-self.bottom, + width = 2*SPACE_WIDTH, + ) + glass.sort_points(lambda p : -p[1]) + glass.shift((self.top-glass.get_top()[1])*UP) + glass.gradient_highlight(self.top_color, self.bottom_color) + return glass + + def generate_layer_info(self): + self.layer_thickness = float(self.top-self.bottom)/self.num_discrete_layers + self.layer_tops = np.arange( + self.top, self.bottom, -self.layer_thickness + ) + top_rgb, bottom_rgb = [ + np.array(Color(color).get_rgb()) + for color in self.top_color, self.bottom_color + ] + epsilon = 1./(self.num_discrete_layers-1) + self.layer_colors = [ + Color(rgb = interpolate(top_rgb, bottom_rgb, alpha)) + for alpha in np.arange(0, 1+epsilon, epsilon) + ] + + def generate_layers(self): + self.generate_layer_info() + def create_region(top, color): + return Region( + lambda x, y : (y < top) & (y > top-self.layer_thickness), + color = color + ) + self.layers = [ + create_region(top, color) + for top, color in zip(self.layer_tops, self.layer_colors) + ] + + + def generate_discrete_path(self): + points = self.cycloid.points + tops = list(self.layer_tops) + tops.append(tops[-1]-self.layer_thickness) + indices = [ + np.argmin(np.abs(points[:, 1]-top)) + for top in tops + ] + self.bend_points = points[indices[1:-1]] + self.path_angles = [] + self.discrete_path = Mobject1D( + color = YELLOW, + density = 3*DEFAULT_POINT_DENSITY_1D + ) + for start, end in zip(indices, indices[1:]): + start_point, end_point = points[start], points[end] + self.discrete_path.add_line( + start_point, end_point + ) + self.path_angles.append( + angle_of_vector(start_point-end_point)-np.pi/2 + ) + self.discrete_path.add_line( + points[end], SPACE_WIDTH*RIGHT+(self.layer_tops[-1]-1)*UP + ) + + def show_layer_variables(self): + layer_top_pairs = zip( + self.layer_tops[:self.num_variables], + self.layer_tops[1:] + ) + v_equations = [] + start_ys = [] + end_ys = [] + center_paths = [] + braces = [] + for (top1, top2), x in zip(layer_top_pairs, it.count(1)): + eq_mob = TexMobject( + ["v_%d"%x, "=", "\sqrt{\phantom{y_1}}"], + size = "\\Large" + ) + midpoint = UP*(top1+top2)/2 + eq_mob.shift(midpoint) + v_eq = eq_mob.split() + center_paths.append(Line( + midpoint+SPACE_WIDTH*LEFT, + midpoint+SPACE_WIDTH*RIGHT + )) + brace_endpoints = Mobject( + Point(self.top*UP+x*RIGHT), + Point(top2*UP+x*RIGHT) + ) + brace = Brace(brace_endpoints, RIGHT) + + start_y = TexMobject("y_%d"%x, size = "\\Large") + end_y = start_y.copy() + start_y.next_to(brace, RIGHT) + end_y.shift(v_eq[-1].get_center()) + end_y.shift(0.2*RIGHT) + + v_equations.append(v_eq) + start_ys.append(start_y) + end_ys.append(end_y) + braces.append(brace) + + for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]): + photon_run = self.photon_run_along_path( + path, + rate_func = None + ) + self.play( + ShimmerIn(v_eq[0]), + photon_run, + run_time = time + ) + self.dither() + for start_y, brace in zip(start_ys, braces): + self.add(start_y) + self.play(GrowFromCenter(brace)) + self.dither() + quads = zip(v_equations, start_ys, end_ys, braces) + self.equations = [] + for v_eq, start_y, end_y, brace in quads: + self.remove(brace) + self.play( + ShowCreation(v_eq[1]), + ShowCreation(v_eq[2]), + Transform(start_y, end_y) + ) + + v_eq.append(start_y) + self.equations.append(Mobject(*v_eq)) + + def isolate_bend_points(self): + arc_radius = 0.1 + self.activate_zooming() + little_square = self.get_zoomed_camera_mobject() + + for index in range(3): + bend_point = self.bend_points[index] + line = Line( + bend_point+DOWN, + bend_point+UP, + color = WHITE, + density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D + ) + angle_arcs = [] + for i, rotation in [(index, np.pi/2), (index+1, -np.pi/2)]: + arc = Arc(angle = self.path_angles[i]) + arc.scale(arc_radius) + arc.rotate(rotation) + arc.shift(bend_point) + angle_arcs.append(arc) + thetas = [] + for i in [index+1, index+2]: + theta = TexMobject("\\theta_%d"%i) + theta.scale(0.5/self.zoom_factor) + vert = UP if i == index+1 else DOWN + horiz = rotate_vector(vert, np.pi/2) + theta.next_to( + Point(bend_point), + horiz, + buff = 0.01 + ) + theta.shift(1.5*arc_radius*vert) + thetas.append(theta) + figure_marks = [line] + angle_arcs + thetas + + self.play(ApplyMethod( + little_square.shift, + bend_point - little_square.get_center(), + run_time = 2 + )) + self.play(*map(ShowCreation, figure_marks)) + self.dither() + equation_frame = little_square.copy() + equation_frame.scale(0.5) + equation_frame.shift( + little_square.get_corner(UP+RIGHT) - \ + equation_frame.get_corner(UP+RIGHT) + ) + equation_frame.scale_in_place(0.9) + self.show_snells(index+1, equation_frame) + self.remove(*figure_marks) + self.disactivate_zooming() + + def show_snells(self, index, frame): + left_text, right_text = [ + "\\dfrac{\\sin(\\theta_%d)}{\\phantom{\\sqrt{y_1}}}"%x + for x in index, index+1 + ] + left, equals, right = TexMobject( + [left_text, "=", right_text] + ).split() + vs = [] + sqrt_ys = [] + for x, numerator in [(index, left), (index+1, right)]: + v, sqrt_y = [ + TexMobject( + text, size = "\\Large" + ).next_to(numerator, DOWN) + for text in "v_%d"%x, "\\sqrt{y_%d}"%x + ] + vs.append(v) + sqrt_ys.append(sqrt_y) + start, end = [ + Mobject( + left.copy(), mobs[0], equals.copy(), right.copy(), mobs[1] + ).replace(frame) + for mobs in vs, sqrt_ys + ] + + self.add(start) + self.dither(2) + self.play(Transform( + start, end, + path_func = counterclockwise_path() + )) + self.dither(2) + self.remove(start, end) + + def show_main_equation(self): + self.equation = TexMobject(""" + \\dfrac{\\sin(\\theta)}{\\sqrt{y}} = + \\text{constant} + """) + self.equation.shift(LEFT) + self.equation.shift( + (self.layer_tops[0]-self.equation.get_top())*UP + ) + self.add(self.equation) + self.dither() + + def ask_continuous_question(self): + continuous = self.get_continuous_background() + line = Line( + UP, DOWN, + density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D + ) + theta = TexMobject("\\theta") + theta.scale(0.5/self.zoom_factor) + + self.play( + ShowCreation(continuous), + Animation(self.equation) + ) + self.remove(*self.layers) + self.play(ShowCreation(self.cycloid)) + self.activate_zooming() + little_square = self.get_zoomed_camera_mobject() + + self.add(line) + indices = np.arange( + 0, self.cycloid.get_num_points()-1, 10 + ) + for index in indices: + point = self.cycloid.points[index] + next_point = self.cycloid.points[index+1] + angle = angle_of_vector(point - next_point) + for mob in little_square, line: + mob.shift(point - mob.get_center()) + arc = Arc(angle-np.pi/2, start_angle = np.pi/2) + arc.scale(0.1) + arc.shift(point) + self.add(arc) + if angle > np.pi/2 + np.pi/6: + vect_angle = interpolate(np.pi/2, angle, 0.5) + vect = rotate_vector(RIGHT, vect_angle) + theta.center() + theta.shift(point) + theta.shift(0.15*vect) + self.add(theta) + self.dither(self.frame_duration) + self.remove(arc) \ No newline at end of file diff --git a/old_projects/brachistochrone/light.py b/old_projects/brachistochrone/light.py new file mode 100644 index 00000000..82f25a4d --- /dev/null +++ b/old_projects/brachistochrone/light.py @@ -0,0 +1,943 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject, Mobject1D +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import * +from topics.characters import Randolph, Mathematician +from topics.functions import * +from topics.number_line import * +from mobject.region import Region, region_from_polygon_vertices +from scene import Scene +from scene.zoomed_scene import ZoomedScene + +from brachistochrone.curves import \ + Cycloid, PathSlidingScene, RANDY_SCALE_VAL, TryManyPaths + + +class Lens(Arc): + CONFIG = { + "radius" : 2, + "angle" : np.pi/2, + "color" : BLUE_B, + } + def __init__(self, **kwargs): + digest_config(self, kwargs) + Arc.__init__(self, self.angle, **kwargs) + + def generate_points(self): + Arc.generate_points(self) + self.rotate(-np.pi/4) + self.shift(-self.get_left()) + self.add_points(self.copy().rotate(np.pi).points) + + + +class PhotonScene(Scene): + def wavify(self, mobject): + result = mobject.copy() + result.ingest_submobjects() + tangent_vectors = result.points[1:]-result.points[:-1] + lengths = np.apply_along_axis( + np.linalg.norm, 1, tangent_vectors + ) + thick_lengths = lengths.repeat(3).reshape((len(lengths), 3)) + unit_tangent_vectors = tangent_vectors/thick_lengths + rot_matrix = np.transpose(rotation_matrix(np.pi/2, OUT)) + normal_vectors = np.dot(unit_tangent_vectors, rot_matrix) + # total_length = np.sum(lengths) + times = np.cumsum(lengths) + nudge_sizes = 0.1*np.sin(2*np.pi*times) + thick_nudge_sizes = nudge_sizes.repeat(3).reshape((len(nudge_sizes), 3)) + nudges = thick_nudge_sizes*normal_vectors + result.points[1:] += nudges + return result + + + def photon_run_along_path(self, path, color = YELLOW, **kwargs): + if "rate_func" not in kwargs: + kwargs["rate_func"] = None + photon = self.wavify(path) + photon.highlight(color) + return ShowPassingFlash(photon, **kwargs) + + +class SimplePhoton(PhotonScene): + def construct(self): + text = TextMobject("Light") + text.to_edge(UP) + self.play(ShimmerIn(text)) + self.play(self.photon_run_along_path( + Cycloid(), rate_func = None + )) + self.dither() + + +class MultipathPhotonScene(PhotonScene): + CONFIG = { + "num_paths" : 5 + } + def run_along_paths(self, **kwargs): + paths = self.get_paths() + colors = Color(YELLOW).range_to(WHITE, len(paths)) + for path, color in zip(paths, colors): + path.highlight(color) + photon_runs = [ + self.photon_run_along_path(path) + for path in paths + ] + for photon_run, path in zip(photon_runs, paths): + self.play( + photon_run, + ShowCreation( + path, + rate_func = lambda t : 0.9*smooth(t) + ), + **kwargs + ) + self.dither() + + def generate_paths(self): + raise Exception("Not Implemented") + + +class PhotonThroughLens(MultipathPhotonScene): + def construct(self): + self.lens = Lens() + self.add(self.lens) + self.run_along_paths() + + + def get_paths(self): + interval_values = np.arange(self.num_paths).astype('float') + interval_values /= (self.num_paths-1.) + first_contact = [ + self.lens.point_from_proportion(0.4*v+0.55) + for v in reversed(interval_values) + ] + second_contact = [ + self.lens.point_from_proportion(0.3*v + 0.1) + for v in interval_values + ] + focal_point = 2*RIGHT + return [ + Mobject( + Line(SPACE_WIDTH*LEFT + fc[1]*UP, fc), + Line(fc, sc), + Line(sc, focal_point), + Line(focal_point, 6*focal_point-5*sc) + ).ingest_submobjects() + for fc, sc in zip(first_contact, second_contact) + ] + +class TransitionToOptics(PhotonThroughLens): + def construct(self): + optics = TextMobject("Optics") + optics.to_edge(UP) + self.add(optics) + self.has_started = False + PhotonThroughLens.construct(self) + + def play(self, *args, **kwargs): + if not self.has_started: + self.has_started = True + everything = Mobject(*self.mobjects) + vect = 2*SPACE_WIDTH*RIGHT + everything.shift(vect) + self.play(ApplyMethod( + everything.shift, -vect, + rate_func = rush_from + )) + Scene.play(self, *args, **kwargs) + + +class PhotonOffMirror(MultipathPhotonScene): + def construct(self): + self.mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP])) + self.mirror.highlight(GREY) + self.add(self.mirror) + self.run_along_paths() + + def get_paths(self): + interval_values = np.arange(self.num_paths).astype('float') + interval_values /= (self.num_paths-1) + anchor_points = [ + self.mirror.point_from_proportion(0.6*v+0.3) + for v in interval_values + ] + start_point = 5*LEFT+3*UP + end_points = [] + for point in anchor_points: + vect = start_point-point + vect[1] *= -1 + end_points.append(point+2*vect) + return [ + Mobject( + Line(start_point, anchor_point), + Line(anchor_point, end_point) + ).ingest_submobjects() + for anchor_point, end_point in zip(anchor_points, end_points) + ] + +class PhotonsInWater(MultipathPhotonScene): + def construct(self): + water = Region(lambda x, y : y < 0, color = BLUE_E) + self.add(water) + self.run_along_paths() + + def get_paths(self): + x, y = -3, 3 + start_point = x*RIGHT + y*UP + angles = np.arange(np.pi/18, np.pi/3, np.pi/18) + midpoints = y*np.arctan(angles) + end_points = midpoints + SPACE_HEIGHT*np.arctan(2*angles) + return [ + Mobject( + Line(start_point, [midpoint, 0, 0]), + Line([midpoint, 0, 0], [end_point, -SPACE_HEIGHT, 0]) + ).ingest_submobjects() + for midpoint, end_point in zip(midpoints, end_points) + ] + + +class ShowMultiplePathsScene(PhotonScene): + def construct(self): + text = TextMobject("Which path minimizes travel time?") + text.to_edge(UP) + self.generate_start_and_end_points() + point_a = Dot(self.start_point) + point_b = Dot(self.end_point) + A = TextMobject("A").next_to(point_a, UP) + B = TextMobject("B").next_to(point_b, DOWN) + paths = self.get_paths() + + for point, letter in [(point_a, A), (point_b, B)]: + self.play( + ShowCreation(point), + ShimmerIn(letter) + ) + self.play(ShimmerIn(text)) + curr_path = paths[0].copy() + curr_path_copy = curr_path.copy().ingest_submobjects() + self.play( + self.photon_run_along_path(curr_path), + ShowCreation(curr_path_copy, rate_func = rush_into) + ) + self.remove(curr_path_copy) + for path in paths[1:] + [paths[0]]: + self.play(Transform(curr_path, path, run_time = 4)) + self.dither() + self.path = curr_path.ingest_submobjects() + + def generate_start_and_end_points(self): + raise Exception("Not Implemented") + + def get_paths(self): + raise Exception("Not implemented") + + +class ShowMultiplePathsThroughLens(ShowMultiplePathsScene): + def construct(self): + self.lens = Lens() + self.add(self.lens) + ShowMultiplePathsScene.construct(self) + + def generate_start_and_end_points(self): + self.start_point = 3*LEFT + UP + self.end_point = 2*RIGHT + + def get_paths(self): + alphas = [0.25, 0.4, 0.58, 0.75] + lower_right, upper_right, upper_left, lower_left = map( + self.lens.point_from_proportion, alphas + ) + return [ + Mobject( + Line(self.start_point, a), + Line(a, b), + Line(b, self.end_point) + ).highlight(color) + for (a, b), color in zip( + [ + (upper_left, upper_right), + (upper_left, lower_right), + (lower_left, lower_right), + (lower_left, upper_right), + ], + Color(YELLOW).range_to(WHITE, 4) + ) + ] + + +class ShowMultiplePathsOffMirror(ShowMultiplePathsScene): + def construct(self): + mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP])) + mirror.highlight(GREY) + self.add(mirror) + ShowMultiplePathsScene.construct(self) + + def generate_start_and_end_points(self): + self.start_point = 4*LEFT + 2*UP + self.end_point = 4*LEFT + 2*DOWN + + def get_paths(self): + return [ + Mobject( + Line(self.start_point, midpoint), + Line(midpoint, self.end_point) + ).highlight(color) + for midpoint, color in zip( + [2*UP, 2*DOWN], + Color(YELLOW).range_to(WHITE, 2) + ) + ] + + +class ShowMultiplePathsInWater(ShowMultiplePathsScene): + def construct(self): + glass = Region(lambda x, y : y < 0, color = BLUE_E) + self.generate_start_and_end_points() + straight = Line(self.start_point, self.end_point) + slow = TextMobject("Slow") + slow.rotate(np.arctan(straight.get_slope())) + slow.shift(straight.points[int(0.7*straight.get_num_points())]) + slow.shift(0.5*DOWN) + too_long = TextMobject("Too long") + too_long.shift(UP) + air = TextMobject("Air").shift(2*UP) + water = TextMobject("Water").shift(2*DOWN) + + self.add(glass) + self.play(GrowFromCenter(air)) + self.play(GrowFromCenter(water)) + self.dither() + self.remove(air, water) + ShowMultiplePathsScene.construct(self) + self.play( + Transform(self.path, straight) + ) + self.dither() + self.play(GrowFromCenter(slow)) + self.dither() + self.remove(slow) + self.leftmost.ingest_submobjects() + self.play(Transform(self.path, self.leftmost, run_time = 3)) + self.dither() + self.play(ShimmerIn(too_long)) + self.dither() + + + def generate_start_and_end_points(self): + self.start_point = 3*LEFT + 2*UP + self.end_point = 3*RIGHT + 2*DOWN + + def get_paths(self): + self.leftmost, self.rightmost = result = [ + Mobject( + Line(self.start_point, midpoint), + Line(midpoint, self.end_point) + ).highlight(color) + for midpoint, color in zip( + [3*LEFT, 3*RIGHT], + Color(YELLOW).range_to(WHITE, 2) + ) + ] + return result + + +class StraightLinesFastestInConstantMedium(PhotonScene): + def construct(self): + kwargs = {"size" : "\\Large"} + left = TextMobject("Speed of light is constant", **kwargs) + arrow = TexMobject("\\Rightarrow", **kwargs) + right = TextMobject("Staight path is fastest", **kwargs) + left.next_to(arrow, LEFT) + right.next_to(arrow, RIGHT) + squaggle, line = self.get_paths() + + self.play(*map(ShimmerIn, [left, arrow, right])) + self.play(ShowCreation(squaggle)) + self.play(self.photon_run_along_path( + squaggle, run_time = 2, rate_func = None + )) + self.play(Transform( + squaggle, line, + path_func = path_along_arc(np.pi) + )) + self.play(self.photon_run_along_path(line, rate_func = None)) + self.dither() + + + def get_paths(self): + squaggle = ParametricFunction( + lambda t : (0.5*t+np.cos(t))*RIGHT+np.sin(t)*UP, + start = -np.pi, + end = 2*np.pi + ) + squaggle.shift(2*UP) + start, end = squaggle.points[0], squaggle.points[-1] + line = Line(start, end) + result = [squaggle, line] + for mob in result: + mob.highlight(BLUE_D) + return result + +class PhtonBendsInWater(PhotonScene, ZoomedScene): + def construct(self): + glass = Region(lambda x, y : y < 0, color = BLUE_E) + kwargs = { + "density" : self.zoom_factor*DEFAULT_POINT_DENSITY_1D + } + top_line = Line(SPACE_HEIGHT*UP+2*LEFT, ORIGIN, **kwargs) + extension = Line(ORIGIN, SPACE_HEIGHT*DOWN+2*RIGHT, **kwargs) + bottom_line = Line(ORIGIN, SPACE_HEIGHT*DOWN+RIGHT, **kwargs) + path1 = Mobject(top_line, extension) + path2 = Mobject(top_line, bottom_line) + for mob in path1, path2: + mob.ingest_submobjects() + extension.highlight(RED) + theta1 = np.arctan(bottom_line.get_slope()) + theta2 = np.arctan(extension.get_slope()) + arc = Arc(theta2-theta1, start_angle = theta1, radius = 2) + question_mark = TextMobject("$\\theta$?") + question_mark.shift(arc.get_center()+0.5*DOWN+0.25*RIGHT) + wave = self.wavify(path2) + wave.highlight(YELLOW) + wave.scale(0.5) + + self.add(glass) + self.play(ShowCreation(path1)) + self.play(Transform(path1, path2)) + self.dither() + # self.activate_zooming() + self.dither() + self.play(ShowPassingFlash( + wave, run_time = 3, rate_func = None + )) + self.dither() + self.play(ShowCreation(extension)) + self.play( + ShowCreation(arc), + ShimmerIn(question_mark) + ) + +class LightIsFasterInAirThanWater(ShowMultiplePathsInWater): + def construct(self): + glass = Region(lambda x, y : y < 0, color = BLUE_E) + equation = TexMobject("v_{\\text{air}} > v_{\\text{water}}") + equation.to_edge(UP) + path = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) + path1 = path.copy().shift(2*UP) + path2 = path.copy().shift(2*DOWN) + + self.add(glass) + self.play(ShimmerIn(equation)) + self.dither() + photon_runs = [] + photon_runs.append(self.photon_run_along_path( + path1, rate_func = lambda t : min(1, 1.2*t) + )) + photon_runs.append(self.photon_run_along_path(path2)) + self.play(*photon_runs, **{"run_time" : 2}) + self.dither() + + +class GeometryOfGlassSituation(ShowMultiplePathsInWater): + def construct(self): + glass = Region(lambda x, y : y < 0, color = BLUE_E) + self.generate_start_and_end_points() + left = self.start_point[0]*RIGHT + right = self.end_point[0]*RIGHT + start_x = interpolate(left, right, 0.2) + end_x = interpolate(left, right, 1.0) + left_line = Line(self.start_point, left, color = RED_D) + right_line = Line(self.end_point, right, color = RED_D) + h_1, h_2 = map(TexMobject, ["h_1", "h_2"]) + h_1.next_to(left_line, LEFT) + h_2.next_to(right_line, RIGHT) + point_a = Dot(self.start_point) + point_b = Dot(self.end_point) + A = TextMobject("A").next_to(point_a, UP) + B = TextMobject("B").next_to(point_b, DOWN) + + x = start_x + left_brace = Brace(Mobject(Point(left), Point(x))) + right_brace = Brace(Mobject(Point(x), Point(right)), UP) + x_mob = TexMobject("x") + x_mob.next_to(left_brace, DOWN) + w_minus_x = TexMobject("w-x") + w_minus_x.next_to(right_brace, UP) + top_line = Line(self.start_point, x) + bottom_line = Line(x, self.end_point) + top_dist = TexMobject("\\sqrt{h_1^2+x^2}") + top_dist.scale(0.5) + a = 0.3 + n = top_line.get_num_points() + point = top_line.points[int(a*n)] + top_dist.next_to(Point(point), RIGHT, buff = 0.3) + bottom_dist = TexMobject("\\sqrt{h_2^2+(w-x)^2}") + bottom_dist.scale(0.5) + n = bottom_line.get_num_points() + point = bottom_line.points[int((1-a)*n)] + bottom_dist.next_to(Point(point), LEFT, buff = 1) + + end_top_line = Line(self.start_point, end_x) + end_bottom_line = Line(end_x, self.end_point) + end_brace = Brace(Mobject(Point(left), Point(end_x))) + end_x_mob = TexMobject("x").next_to(end_brace, DOWN) + + axes = Mobject( + NumberLine(), + NumberLine().rotate(np.pi/2).shift(7*LEFT) + ) + graph = FunctionGraph( + lambda x : 0.4*(x+1)*(x-3)+4, + x_min = -2, + x_max = 4 + ) + graph.highlight(YELLOW) + Mobject(axes, graph).scale(0.2).to_corner(UP+RIGHT, buff = 1) + axes.add(TexMobject("x", size = "\\small").next_to(axes, RIGHT)) + axes.add(TextMobject("Travel time", size = "\\small").next_to( + axes, UP + )) + new_graph = graph.copy() + midpoint = new_graph.points[new_graph.get_num_points()/2] + new_graph.filter_out(lambda p : p[0] < midpoint[0]) + new_graph.reverse_points() + pairs_for_end_transform = [ + (mob, mob.copy()) + for mob in top_line, bottom_line, left_brace, x_mob + ] + + self.add(glass, point_a, point_b, A, B) + line = Mobject(top_line, bottom_line).ingest_submobjects() + self.play(ShowCreation(line)) + self.dither() + self.play( + GrowFromCenter(left_brace), + GrowFromCenter(x_mob) + ) + self.play( + GrowFromCenter(right_brace), + GrowFromCenter(w_minus_x) + ) + self.play(ShowCreation(left_line), ShimmerIn(h_1)) + self.play(ShowCreation(right_line), GrowFromCenter(h_2)) + self.play(ShimmerIn(top_dist)) + self.play(GrowFromCenter(bottom_dist)) + self.dither(3) + self.clear() + self.add(glass, point_a, point_b, A, B, + top_line, bottom_line, left_brace, x_mob) + self.play(ShowCreation(axes)) + kwargs = { + "run_time" : 4, + } + self.play(*[ + Transform(*pair, **kwargs) + for pair in [ + (top_line, end_top_line), + (bottom_line, end_bottom_line), + (left_brace, end_brace), + (x_mob, end_x_mob) + ] + ]+[ShowCreation(graph, **kwargs)]) + self.dither() + self.show_derivatives(graph) + line = self.show_derivatives(new_graph) + self.add(line) + self.play(*[ + Transform(*pair, rate_func = lambda x : 0.3*smooth(x)) + for pair in pairs_for_end_transform + ]) + self.dither() + + def show_derivatives(self, graph, run_time = 2): + step = self.frame_duration/run_time + for a in smooth(np.arange(0, 1-step, step)): + index = int(a*graph.get_num_points()) + p1, p2 = graph.points[index], graph.points[index+1] + line = Line(LEFT, RIGHT) + line.rotate(angle_of_vector(p2-p1)) + line.shift(p1) + self.add(line) + self.dither(self.frame_duration) + self.remove(line) + return line + + +class Spring(Line): + CONFIG = { + "num_loops" : 5, + "loop_radius" : 0.3, + "color" : GREY + } + + def generate_points(self): + ## self.start, self.end + length = np.linalg.norm(self.end-self.start) + angle = angle_of_vector(self.end-self.start) + micro_radius = self.loop_radius/length + m = 2*np.pi*(self.num_loops+0.5) + def loop(t): + return micro_radius*( + RIGHT + np.cos(m*t)*LEFT + np.sin(m*t)*UP + ) + new_epsilon = self.epsilon/(m*micro_radius)/length + + self.add_points([ + t*RIGHT + loop(t) + for t in np.arange(0, 1, new_epsilon) + ]) + self.scale(length/(1+2*micro_radius)) + self.rotate(angle) + self.shift(self.start) + + +class SpringSetup(ShowMultiplePathsInWater): + def construct(self): + self.ring_shift_val = 6*RIGHT + self.slide_kwargs = { + "rate_func" : there_and_back, + "run_time" : 5 + } + + self.setup_background() + rod = Region( + lambda x, y : (abs(x) < 5) & (abs(y) < 0.05), + color = GOLD_E + ) + ring = Arc( + angle = 11*np.pi/6, + start_angle = -11*np.pi/12, + radius = 0.2, + color = YELLOW + ) + ring.shift(-self.ring_shift_val/2) + self.generate_springs(ring) + + + self.add_rod_and_ring(rod, ring) + self.slide_ring(ring) + self.dither() + self.add_springs() + self.add_force_definitions() + self.slide_system(ring) + self.show_horizontal_component(ring) + self.show_angles(ring) + self.show_equation() + + + def setup_background(self): + glass = Region(lambda x, y : y < 0, color = BLUE_E) + self.generate_start_and_end_points() + point_a = Dot(self.start_point) + point_b = Dot(self.end_point) + A = TextMobject("A").next_to(point_a, UP) + B = TextMobject("B").next_to(point_b, DOWN) + self.add(glass, point_a, point_b, A, B) + + def generate_springs(self, ring): + self.start_springs, self.end_springs = [ + Mobject( + Spring(self.start_point, r.get_top()), + Spring(self.end_point, r.get_bottom()) + ) + for r in ring, ring.copy().shift(self.ring_shift_val) + ] + + def add_rod_and_ring(self, rod, ring): + rod_word = TextMobject("Rod") + rod_word.next_to(Point(), UP) + ring_word = TextMobject("Ring") + ring_word.next_to(ring, UP) + self.dither() + self.add(rod) + self.play(ShimmerIn(rod_word)) + self.dither() + self.remove(rod_word) + self.play(ShowCreation(ring)) + self.play(ShimmerIn(ring_word)) + self.dither() + self.remove(ring_word) + + def slide_ring(self, ring): + self.play(ApplyMethod( + ring.shift, self.ring_shift_val, + **self.slide_kwargs + )) + + def add_springs(self): + colors = iter([BLACK, BLUE_E]) + for spring in self.start_springs.split(): + circle = Circle(color = colors.next()) + circle.reverse_points() + circle.scale(spring.loop_radius) + circle.shift(spring.points[0]) + + self.play(Transform(circle, spring)) + self.remove(circle) + self.add(spring) + self.dither() + + def add_force_definitions(self): + top_force = TexMobject("F_1 = \\dfrac{1}{v_{\\text{air}}}") + bottom_force = TexMobject("F_2 = \\dfrac{1}{v_{\\text{water}}}") + top_spring, bottom_spring = self.start_springs.split() + top_force.next_to(top_spring) + bottom_force.next_to(bottom_spring, DOWN, buff = -0.5) + words = TextMobject(""" + The force in a real spring is + proportional to that spring's length + """) + words.to_corner(UP+RIGHT) + for force in top_force, bottom_force: + self.play(GrowFromCenter(force)) + self.dither() + self.play(ShimmerIn(words)) + self.dither(3) + self.remove(top_force, bottom_force, words) + + def slide_system(self, ring): + equilibrium_slide_kwargs = dict(self.slide_kwargs) + def jiggle_to_equilibrium(t): + return 0.7*(1+((1-t)**2)*(-np.cos(10*np.pi*t))) + equilibrium_slide_kwargs = { + "rate_func" : jiggle_to_equilibrium, + "run_time" : 3 + } + start = Mobject(ring, self.start_springs) + end = Mobject( + ring.copy().shift(self.ring_shift_val), + self.end_springs + ) + for kwargs in self.slide_kwargs, equilibrium_slide_kwargs: + self.play(Transform(start, end, **kwargs)) + self.dither() + + def show_horizontal_component(self, ring): + v_right = Vector(ring.get_top(), RIGHT) + v_left = Vector(ring.get_bottom(), LEFT) + self.play(*map(ShowCreation, [v_right, v_left])) + self.dither() + self.remove(v_right, v_left) + + def show_angles(self, ring): + ring_center = ring.get_center() + lines, arcs, thetas = [], [], [] + counter = it.count(1) + for point in self.start_point, self.end_point: + line = Line(point, ring_center, color = GREY) + angle = np.pi/2-np.abs(np.arctan(line.get_slope())) + arc = Arc(angle, radius = 0.5).rotate(np.pi/2) + if point is self.end_point: + arc.rotate(np.pi) + theta = TexMobject("\\theta_%d"%counter.next()) + theta.scale(0.5) + theta.shift(2*arc.get_center()) + arc.shift(ring_center) + theta.shift(ring_center) + + lines.append(line) + arcs.append(arc) + thetas.append(theta) + vert_line = Line(2*UP, 2*DOWN) + vert_line.shift(ring_center) + top_spring, bottom_spring = self.start_springs.split() + + self.play( + Transform(ring, Point(ring_center)), + Transform(top_spring, lines[0]), + Transform(bottom_spring, lines[1]) + ) + self.play(ShowCreation(vert_line)) + anims = [] + for arc, theta in zip(arcs, thetas): + anims += [ + ShowCreation(arc), + GrowFromCenter(theta) + ] + self.play(*anims) + self.dither() + + def show_equation(self): + equation = TexMobject([ + "\\left(\\dfrac{1}{\\phantom{v_air}}\\right)", + "\\sin(\\theta_1)", + "=", + "\\left(\\dfrac{1}{\\phantom{v_water}}\\right)", + "\\sin(\\theta_2)" + ]) + equation.to_corner(UP+RIGHT) + frac1, sin1, equals, frac2, sin2 = equation.split() + v_air, v_water = [ + TexMobject("v_{\\text{%s}}"%s, size = "\\Large") + for s in "air", "water" + ] + v_air.next_to(Point(frac1.get_center()), DOWN) + v_water.next_to(Point(frac2.get_center()), DOWN) + frac1.add(v_air) + frac2.add(v_water) + f1, f2 = [ + TexMobject("F_%d"%d, size = "\\Large") + for d in 1, 2 + ] + f1.next_to(sin1, LEFT) + f2.next_to(equals, RIGHT) + sin2_start = sin2.copy().next_to(f2, RIGHT) + bar1 = TexMobject("\\dfrac{\\qquad}{\\qquad}") + bar2 = bar1.copy() + bar1.next_to(sin1, DOWN) + bar2.next_to(sin2, DOWN) + v_air_copy = v_air.copy().next_to(bar1, DOWN) + v_water_copy = v_water.copy().next_to(bar2, DOWN) + bars = Mobject(bar1, bar2) + new_eq = equals.copy().center().shift(bars.get_center()) + snells = TextMobject("Snell's Law") + snells.highlight(YELLOW) + snells.shift(new_eq.get_center()[0]*RIGHT) + snells.shift(UP) + + anims = [] + for mob in f1, sin1, equals, f2, sin2_start: + anims.append(ShimmerIn(mob)) + self.play(*anims) + self.dither() + for f, frac in (f1, frac1), (f2, frac2): + target = frac.copy().ingest_submobjects() + also = [] + if f is f2: + also.append(Transform(sin2_start, sin2)) + sin2 = sin2_start + self.play(Transform(f, target), *also) + self.remove(f) + self.add(frac) + self.dither() + self.play( + FadeOut(frac1), + FadeOut(frac2), + Transform(v_air, v_air_copy), + Transform(v_water, v_water_copy), + ShowCreation(bars), + Transform(equals, new_eq) + ) + self.dither() + frac1 = Mobject(sin1, bar1, v_air) + frac2 = Mobject(sin2, bar2, v_water) + for frac, vect in (frac1, LEFT), (frac2, RIGHT): + self.play(ApplyMethod( + frac.next_to, equals, vect + )) + self.dither() + self.play(ShimmerIn(snells)) + self.dither() + +class WhatGovernsTheSpeedOfLight(PhotonScene, PathSlidingScene): + def construct(self): + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + self.add_cycloid_end_points() + + self.add(self.cycloid) + self.slide(randy, self.cycloid) + self.play(self.photon_run_along_path(self.cycloid)) + + self.dither() + +class WhichPathWouldLightTake(PhotonScene, TryManyPaths): + def construct(self): + words = TextMobject( + ["Which path ", "would \\emph{light} take", "?"] + ) + words.split()[1].highlight(YELLOW) + words.to_corner(UP+RIGHT) + self.add_cycloid_end_points() + + anims = [ + self.photon_run_along_path( + path, + rate_func = smooth + ) + for path in self.get_paths() + ] + self.play(anims[0], ShimmerIn(words)) + for anim in anims[1:]: + self.play(anim) + + + +class StateSnellsLaw(PhotonScene): + def construct(self): + point_a = 3*LEFT+3*UP + point_b = 1.5*RIGHT+3*DOWN + midpoint = ORIGIN + + lines, arcs, thetas = [], [], [] + counter = it.count(1) + for point in point_a, point_b: + line = Line(point, midpoint, color = RED_D) + angle = np.pi/2-np.abs(np.arctan(line.get_slope())) + arc = Arc(angle, radius = 0.5).rotate(np.pi/2) + if point is point_b: + arc.rotate(np.pi) + line.reverse_points() + theta = TexMobject("\\theta_%d"%counter.next()) + theta.scale(0.5) + theta.shift(2*arc.get_center()) + arc.shift(midpoint) + theta.shift(midpoint) + + lines.append(line) + arcs.append(arc) + thetas.append(theta) + vert_line = Line(2*UP, 2*DOWN) + vert_line.shift(midpoint) + path = Mobject(*lines).ingest_submobjects() + glass = Region(lambda x, y : y < 0, color = BLUE_E) + self.add(glass) + equation = TexMobject([ + "\\dfrac{\\sin(\\theta_1)}{v_{\\text{air}}}", + "=", + "\\dfrac{\\sin(\\theta_2)}{v_{\\text{water}}}", + ]) + equation.to_corner(UP+RIGHT) + exp1, equals, exp2 = equation.split() + snells_law = TextMobject("Snell's Law:") + snells_law.highlight(YELLOW) + snells_law.to_edge(UP) + + self.play(ShimmerIn(snells_law)) + self.dither() + self.play(ShowCreation(path)) + self.play(self.photon_run_along_path(path)) + self.dither() + self.play(ShowCreation(vert_line)) + self.play(*map(ShowCreation, arcs)) + self.play(*map(GrowFromCenter, thetas)) + self.dither() + self.play(ShimmerIn(exp1)) + self.dither() + self.play(*map(ShimmerIn, [equals, exp2])) + self.dither() + + + + + + + + + + diff --git a/old_projects/brachistochrone/misc.py b/old_projects/brachistochrone/misc.py new file mode 100644 index 00000000..13d07308 --- /dev/null +++ b/old_projects/brachistochrone/misc.py @@ -0,0 +1,512 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import * +from topics.characters import * +from topics.functions import ParametricFunction, FunctionGraph +from topics.number_line import * +from mobject.region import Region, region_from_polygon_vertices +from topics.three_dimensions import Stars +from scene import Scene + +from brachistochrone.curves import Cycloid + +class PhysicalIntuition(Scene): + def construct(self): + n_terms = 4 + def func((x, y, ignore)): + z = complex(x, y) + if (np.abs(x%1 - 0.5)<0.01 and y < 0.01) or np.abs(z)<0.01: + return ORIGIN + out_z = 1./(2*np.tan(np.pi*z)*(z**2)) + return out_z.real*RIGHT - out_z.imag*UP + arrows = Mobject(*[ + Arrow(ORIGIN, np.sqrt(2)*point) + for point in compass_directions(4, RIGHT+UP) + ]) + arrows.highlight(YELLOW) + arrows.ingest_submobjects() + all_arrows = Mobject(*[ + arrows.copy().scale(0.3/(x)).shift(x*RIGHT) + for x in range(1, n_terms+2) + ]) + terms = TexMobject([ + "\\dfrac{1}{%d^2} + "%(x+1) + for x in range(n_terms) + ]+["\\cdots"]) + terms.shift(2*UP) + plane = NumberPlane(color = BLUE_E) + axes = Mobject(NumberLine(), NumberLine().rotate(np.pi/2)) + axes.highlight(WHITE) + + for term in terms.split(): + self.play(ShimmerIn(term, run_time = 0.5)) + self.dither() + self.play(ShowCreation(plane), ShowCreation(axes)) + self.play(*[ + Transform(*pair) + for pair in zip(terms.split(), all_arrows.split()) + ]) + self.play(PhaseFlow( + func, plane, + run_time = 5, + virtual_time = 8 + )) + + + +class TimeLine(Scene): + def construct(self): + dated_events = [ + { + "date" : 1696, + "text": "Johann Bernoulli poses Brachistochrone problem", + "picture" : "Johann_Bernoulli2" + }, + { + "date" : 1662, + "text" : "Fermat states his principle of least time", + "picture" : "Pierre_de_Fermat" + } + ] + speical_dates = [2016] + [ + obj["date"] for obj in dated_events + ] + centuries = range(1600, 2100, 100) + timeline = NumberLine( + numerical_radius = 300, + number_at_center = 1800, + unit_length_to_spatial_width = SPACE_WIDTH/100, + tick_frequency = 10, + numbers_with_elongated_ticks = centuries + ) + timeline.add_numbers(*centuries) + centers = [ + Point(timeline.number_to_point(year)) + for year in speical_dates + ] + timeline.add(*centers) + timeline.shift(-centers[0].get_center()) + + self.add(timeline) + self.dither() + run_times = iter([3, 1]) + for point, event in zip(centers[1:], dated_events): + self.play(ApplyMethod( + timeline.shift, -point.get_center(), + run_time = run_times.next() + )) + picture = ImageMobject(event["picture"], invert = False) + picture.scale_to_fit_width(2) + picture.to_corner(UP+RIGHT) + event_mob = TextMobject(event["text"]) + event_mob.shift(2*LEFT+2*UP) + date_mob = TexMobject(str(event["date"])) + date_mob.scale(0.5) + date_mob.shift(0.6*UP) + line = Line(event_mob.get_bottom(), 0.2*UP) + self.play( + ShimmerIn(event_mob), + ShowCreation(line), + ShimmerIn(date_mob) + ) + self.play(FadeIn(picture)) + self.dither(3) + self.play(*map(FadeOut, [event_mob, date_mob, line, picture])) + + +class StayedUpAllNight(Scene): + def construct(self): + clock = Circle(radius = 2, color = WHITE) + clock.add(Dot(ORIGIN)) + ticks = Mobject(*[ + Line(1.8*vect, 2*vect, color = GREY) + for vect in compass_directions(12) + ]) + clock.add(ticks) + hour_hand = Line(ORIGIN, UP) + minute_hand = Line(ORIGIN, 1.5*UP) + clock.add(hour_hand, minute_hand) + clock.to_corner(UP+RIGHT) + hour_hand.get_center = lambda : clock.get_center() + minute_hand.get_center = lambda : clock.get_center() + + solution = ImageMobject( + "Newton_brachistochrone_solution2", + use_cache = False + ) + solution.stroke_width = 3 + solution.highlight(GREY) + solution.scale_to_fit_width(5) + solution.to_corner(UP+RIGHT) + newton = ImageMobject("Old_Newton", invert = False) + newton.scale(0.8) + phil_trans = TextMobject("Philosophical Transactions") + rect = Rectangle(height = 6, width = 4.5, color = WHITE) + rect.to_corner(UP+RIGHT) + rect.shift(DOWN) + phil_trans.scale_to_fit_width(0.8*rect.get_width()) + phil_trans.next_to(Point(rect.get_top()), DOWN) + new_solution = solution.copy() + new_solution.scale_to_fit_width(phil_trans.get_width()) + new_solution.next_to(phil_trans, DOWN, buff = 1) + not_newton = TextMobject("-Totally not by Newton") + not_newton.scale_to_fit_width(2.5) + not_newton.next_to(new_solution, DOWN, aligned_edge = RIGHT) + phil_trans.add(rect) + + newton_complaint = TextMobject([ + "``I do not love to be", + " \\emph{dunned} ", + "and teased by foreigners''" + ], size = "\\small") + newton_complaint.to_edge(UP, buff = 0.2) + dunned = newton_complaint.split()[1] + dunned.highlight() + dunned_def = TextMobject("(old timey term for making \\\\ demands on someone)") + dunned_def.scale(0.7) + dunned_def.next_to(phil_trans, LEFT) + dunned_def.shift(2*UP) + dunned_arrow = Arrow(dunned_def, dunned) + + johann = ImageMobject("Johann_Bernoulli2", invert = False) + johann.scale(0.4) + johann.to_edge(LEFT) + johann.shift(DOWN) + johann_quote = TextMobject("``I recognize the lion by his claw''") + johann_quote.next_to(johann, UP, aligned_edge = LEFT) + + self.play(ApplyMethod(newton.to_edge, LEFT)) + self.play(ShowCreation(clock)) + kwargs = { + "axis" : OUT, + "rate_func" : smooth + } + self.play( + Rotating(hour_hand, radians = -2*np.pi, **kwargs), + Rotating(minute_hand, radians = -12*2*np.pi, **kwargs), + run_time = 5 + ) + self.dither() + self.clear() + self.add(newton) + clock.ingest_submobjects() + self.play(Transform(clock, solution)) + self.remove(clock) + self.add(solution) + self.dither() + self.play( + FadeIn(phil_trans), + Transform(solution, new_solution) + ) + self.dither() + self.play(ShimmerIn(not_newton)) + phil_trans.add(solution, not_newton) + self.dither() + self.play(*map(ShimmerIn, newton_complaint.split())) + self.dither() + self.play( + ShimmerIn(dunned_def), + ShowCreation(dunned_arrow) + ) + self.dither() + self.remove(dunned_def, dunned_arrow) + self.play(FadeOut(newton_complaint)) + self.remove(newton_complaint) + self.play( + FadeOut(newton), + GrowFromCenter(johann) + ) + self.remove(newton) + self.dither() + self.play(ShimmerIn(johann_quote)) + self.dither() + + +class ThetaTGraph(Scene): + def construct(self): + t_axis = NumberLine() + theta_axis = NumberLine().rotate(np.pi/2) + theta_mob = TexMobject("\\theta(t)") + t_mob = TexMobject("t") + theta_mob.next_to(theta_axis, RIGHT) + theta_mob.to_edge(UP) + t_mob.next_to(t_axis, UP) + t_mob.to_edge(RIGHT) + graph = ParametricFunction( + lambda t : 4*t*RIGHT + 2*smooth(t)*UP + ) + line = Line(graph.points[0], graph.points[-1], color = WHITE) + q_mark = TextMobject("?") + q_mark.next_to(Point(graph.get_center()), LEFT) + stars = Stars(color = BLACK) + stars.scale(0.1).shift(q_mark.get_center()) + + + squiggle = ParametricFunction( + lambda t : t*RIGHT + 0.2*t*(5-t)*(np.sin(t)**2)*UP, + start = 0, + end = 5 + ) + + self.play( + ShowCreation(t_axis), + ShowCreation(theta_axis), + ShimmerIn(theta_mob), + ShimmerIn(t_mob) + ) + self.play( + ShimmerIn(q_mark), + ShowCreation(graph) + ) + self.dither() + self.play( + Transform(q_mark, stars), + Transform(graph, line) + ) + self.dither() + self.play(Transform(graph, squiggle)) + self.dither() + + +class SolutionsToTheBrachistochrone(Scene): + def construct(self): + r_range = np.arange(0.5, 2, 0.25) + cycloids = Mobject(*[ + Cycloid(radius = r, end_theta=2*np.pi) + for r in r_range + ]) + lower_left = 2*DOWN+6*LEFT + lines = Mobject(*[ + Line( + lower_left, + lower_left+5*r*np.cos(np.arctan(r))*RIGHT+2*r*np.sin(np.arctan(r))*UP + ) + for r in r_range + ]) + nl = NumberLine(numbers_with_elongated_ticks = []) + x_axis = nl.copy().shift(3*UP) + y_axis = nl.copy().rotate(np.pi/2).shift(6*LEFT) + t_axis = nl.copy().shift(2*DOWN) + x_label = TexMobject("x") + x_label.next_to(x_axis, DOWN) + x_label.to_edge(RIGHT) + y_label = TexMobject("y") + y_label.next_to(y_axis, RIGHT) + y_label.shift(2*DOWN) + t_label = TexMobject("t") + t_label.next_to(t_axis, UP) + t_label.to_edge(RIGHT) + theta_label = TexMobject("\\theta") + theta_label.next_to(y_axis, RIGHT) + theta_label.to_edge(UP) + words = TextMobject("Boundary conditions?") + words.next_to(lines, RIGHT) + words.shift(2*UP) + + self.play(ShowCreation(x_axis), ShimmerIn(x_label)) + self.play(ShowCreation(y_axis), ShimmerIn(y_label)) + self.play(ShowCreation(cycloids)) + self.dither() + self.play( + Transform(cycloids, lines), + Transform(x_axis, t_axis), + Transform(x_label, t_label), + Transform(y_label, theta_label), + run_time = 2 + ) + self.dither() + self.play(ShimmerIn(words)) + self.dither() + + +class VideoLayout(Scene): + def construct(self): + left, right = 5*LEFT, 5*RIGHT + top_words = TextMobject("The next 15 minutes of your life:") + top_words.to_edge(UP) + line = Line(left, right, color = BLUE_D) + for a in np.arange(0, 4./3, 1./3): + vect = interpolate(left, right, a) + line.add_line(vect+0.2*DOWN, vect+0.2*UP) + left_brace = Brace( + Mobject( + Point(left), + Point(interpolate(left, right, 2./3)) + ), + DOWN + ) + right_brace = Brace( + Mobject( + Point(interpolate(left, right, 2./3)), + Point(right) + ), + UP + ) + left_brace.words = map(TextMobject, [ + "Problem statement", + "History", + "Johann Bernoulli's cleverness" + ]) + curr = left_brace + right_brace.words = map(TextMobject, [ + "Challenge", + "Mark Levi's cleverness", + ]) + for brace in left_brace, right_brace: + curr = brace + direction = DOWN if brace is left_brace else UP + for word in brace.words: + word.next_to(curr, direction) + curr = word + right_brace.words.reverse() + + self.play(ShimmerIn(top_words)) + self.play(ShowCreation(line)) + for brace in left_brace, right_brace: + self.play(GrowFromCenter(brace)) + self.dither() + for word in brace.words: + self.play(ShimmerIn(word)) + self.dither() + + + + +class ShortestPathProblem(Scene): + def construct(self): + point_a, point_b = 3*LEFT, 3*RIGHT + dots = [] + for point, char in [(point_a, "A"), (point_b, "B")]: + dot = Dot(point) + letter = TexMobject(char) + letter.next_to(dot, UP+LEFT) + dot.add(letter) + dots.append(dot) + + path = ParametricFunction( + lambda t : (t/2 + np.cos(t))*RIGHT + np.sin(t)*UP, + start = -2*np.pi, + end = 2*np.pi + ) + path.scale(6/(2*np.pi)) + path.shift(point_a - path.points[0]) + path.highlight(RED) + line = Line(point_a, point_b) + words = TextMobject("Shortest path from $A$ to $B$") + words.to_edge(UP) + + self.play( + ShimmerIn(words), + *map(GrowFromCenter, dots) + ) + self.play(ShowCreation(path)) + self.play(Transform( + path, line, + path_func = path_along_arc(np.pi) + )) + self.dither() + + +class MathBetterThanTalking(Scene): + def construct(self): + mathy = Mathematician() + mathy.to_corner(DOWN+LEFT) + bubble = ThoughtBubble() + bubble.pin_to(mathy) + bubble.write("Math $>$ Talking about math") + + self.add(mathy) + self.play(ShowCreation(bubble)) + self.play(ShimmerIn(bubble.content)) + self.dither() + self.play(ApplyMethod( + mathy.blink, + rate_func = squish_rate_func(there_and_back, 0.4, 0.6) + )) + + +class DetailsOfProofBox(Scene): + def construct(self): + rect = Rectangle(height = 4, width = 6, color = WHITE) + words = TextMobject("Details of proof") + words.to_edge(UP) + + self.play( + ShowCreation(rect), + ShimmerIn(words) + ) + self.dither() + + + +class TalkedAboutSnellsLaw(Scene): + def construct(self): + randy = Randolph() + randy.to_corner(DOWN+LEFT) + morty = Mortimer() + morty.to_edge(DOWN+RIGHT) + randy.bubble = SpeechBubble().pin_to(randy) + morty.bubble = SpeechBubble().pin_to(morty) + + phrases = [ + "Let's talk about Snell's law", + "I love Snell's law", + "It's like running from \\\\ a beach into the ocean", + "It's like two constant \\\\ tension springs", + ] + + self.add(randy, morty) + talkers = it.cycle([randy, morty]) + for talker, phrase in zip(talkers, phrases): + talker.bubble.write(phrase) + self.play( + FadeIn(talker.bubble), + ShimmerIn(talker.bubble.content) + ) + self.play(ApplyMethod( + talker.blink, + rate_func = squish_rate_func(there_and_back) + )) + self.dither() + self.remove(talker.bubble, talker.bubble.content) + + +class YetAnotherMarkLevi(Scene): + def construct(self): + words = TextMobject("Yet another bit of Mark Levi cleverness") + words.to_edge(UP) + levi = ImageMobject("Mark_Levi", invert = False) + levi.scale_to_fit_width(6) + levi.show() + + self.add(levi) + self.play(ShimmerIn(words)) + self.dither(2) + + + + + + + + + + + + + + + + diff --git a/old_projects/brachistochrone/multilayered.py b/old_projects/brachistochrone/multilayered.py new file mode 100644 index 00000000..2755fdcd --- /dev/null +++ b/old_projects/brachistochrone/multilayered.py @@ -0,0 +1,498 @@ +import numpy as np +import itertools as it + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject, Mobject1D +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from topics.geometry import * +from topics.characters import Randolph +from topics.functions import * +from mobject.region import Region +from scene import Scene +from scene.zoomed_scene import ZoomedScene + +from camera import Camera +from brachistochrone.light import PhotonScene +from brachistochrone.curves import * + + +class MultilayeredScene(Scene): + CONFIG = { + "n_layers" : 5, + "top_color" : BLUE_E, + "bottom_color" : BLUE_A, + "total_glass_height" : 5, + "top" : 3*UP, + "RectClass" : Rectangle #FilledRectangle + } + + def get_layers(self, n_layers = None): + if n_layers is None: + n_layers = self.n_layers + width = 2*SPACE_WIDTH + height = float(self.total_glass_height)/n_layers + rgb_pair = [ + np.array(Color(color).get_rgb()) + for color in self.top_color, self.bottom_color + ] + rgb_range = [ + interpolate(*rgb_pair+[x]) + for x in np.arange(0, 1, 1./n_layers) + ] + tops = [ + self.top + x*height*DOWN + for x in range(n_layers) + ] + color = Color() + result = [] + for top, rgb in zip(tops, rgb_range): + color.set_rgb(rgb) + rect = self.RectClass( + height = height, + width = width, + color = color + ) + rect.shift(top-rect.get_top()) + result.append(rect) + return result + + def add_layers(self): + self.layers = self.get_layers() + self.add(*self.layers) + self.freeze_background() + + def get_bottom(self): + return self.top + self.total_glass_height*DOWN + + def get_continuous_glass(self): + result = self.RectClass( + width = 2*SPACE_WIDTH, + height = self.total_glass_height, + ) + result.sort_points(lambda p : -p[1]) + result.gradient_highlight(self.top_color, self.bottom_color) + result.shift(self.top-result.get_top()) + return result + + +class TwoToMany(MultilayeredScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + glass = self.get_glass() + layers = self.get_layers() + + self.add(glass) + self.dither() + self.play(*[ + FadeIn( + layer, + rate_func = squish_rate_func(smooth, x, 1) + ) + for layer, x in zip(layers[1:], it.count(0, 0.2)) + ]+[ + Transform(glass, layers[0]) + ]) + self.dither() + + def get_glass(self): + return self.RectClass( + height = SPACE_HEIGHT, + width = 2*SPACE_WIDTH, + color = BLUE_E + ).shift(SPACE_HEIGHT*DOWN/2) + + +class RaceLightInLayers(MultilayeredScene, PhotonScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + self.add_layers() + line = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) + lines = [ + line.copy().shift(layer.get_center()) + for layer in self.layers + ] + + def rate_maker(x): + return lambda t : min(x*x*t, 1) + min_rate, max_rate = 1., 2. + rates = np.arange(min_rate, max_rate, (max_rate-min_rate)/self.n_layers) + self.play(*[ + self.photon_run_along_path( + line, + rate_func = rate_maker(rate), + run_time = 2 + ) + for line, rate in zip(lines, rates) + ]) + +class ShowDiscretePath(MultilayeredScene, PhotonScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + self.add_layers() + self.cycloid = Cycloid(end_theta = np.pi) + + self.generate_discrete_path() + self.play(ShowCreation(self.discrete_path)) + self.dither() + self.play(self.photon_run_along_path( + self.discrete_path, + rate_func = rush_into, + run_time = 3 + )) + self.dither() + + + def generate_discrete_path(self): + points = self.cycloid.points + tops = [mob.get_top()[1] for mob in self.layers] + tops.append(tops[-1]-self.layers[0].get_height()) + indices = [ + np.argmin(np.abs(points[:, 1]-top)) + for top in tops + ] + self.bend_points = points[indices[1:-1]] + self.path_angles = [] + self.discrete_path = Mobject1D( + color = WHITE, + density = 3*DEFAULT_POINT_DENSITY_1D + ) + for start, end in zip(indices, indices[1:]): + start_point, end_point = points[start], points[end] + self.discrete_path.add_line( + start_point, end_point + ) + self.path_angles.append( + angle_of_vector(start_point-end_point)-np.pi/2 + ) + self.discrete_path.add_line( + points[end], SPACE_WIDTH*RIGHT+(tops[-1]-0.5)*UP + ) + +class NLayers(MultilayeredScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + self.add_layers() + brace = Brace( + Mobject( + Point(self.top), + Point(self.get_bottom()) + ), + RIGHT + ) + n_layers = TextMobject("$n$ layers") + n_layers.next_to(brace) + + self.dither() + + self.add(brace) + self.show_frame() + + self.play( + GrowFromCenter(brace), + GrowFromCenter(n_layers) + ) + self.dither() + +class ShowLayerVariables(MultilayeredScene, PhotonScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + self.add_layers() + v_equations = [] + start_ys = [] + end_ys = [] + center_paths = [] + braces = [] + for layer, x in zip(self.layers[:3], it.count(1)): + eq_mob = TexMobject( + ["v_%d"%x, "=", "\sqrt{\phantom{y_1}}"], + size = "\\Large" + ) + eq_mob.shift(layer.get_center()+2*LEFT) + v_eq = eq_mob.split() + v_eq[0].highlight(layer.get_color()) + path = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) + path.shift(layer.get_center()) + brace_endpoints = Mobject( + Point(self.top), + Point(layer.get_bottom()) + ) + brace = Brace(brace_endpoints, RIGHT) + brace.shift(x*RIGHT) + + start_y = TexMobject("y_%d"%x, size = "\\Large") + end_y = start_y.copy() + start_y.next_to(brace, RIGHT) + end_y.shift(v_eq[-1].get_center()) + nudge = 0.2*RIGHT + end_y.shift(nudge) + + v_equations.append(v_eq) + start_ys.append(start_y) + end_ys.append(end_y) + center_paths.append(path) + braces.append(brace) + + for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]): + photon_run = self.photon_run_along_path( + path, + rate_func = None + ) + self.play( + FadeToColor(v_eq[0], WHITE), + photon_run, + run_time = time + ) + self.dither() + + starts = [0, 0.3, 0.6] + self.play(*it.chain(*[ + [ + GrowFromCenter( + mob, + rate_func=squish_rate_func(smooth, start, 1) + ) + for mob, start in zip(mobs, starts) + ] + for mobs in start_ys, braces + ])) + self.dither() + + triplets = zip(v_equations, start_ys, end_ys) + anims = [] + for v_eq, start_y, end_y in triplets: + anims += [ + ShowCreation(v_eq[1]), + ShowCreation(v_eq[2]), + Transform(start_y.copy(), end_y) + ] + self.play(*anims) + self.dither() + + +class LimitingProcess(MultilayeredScene): + CONFIG = { + "RectClass" : FilledRectangle + } + def construct(self): + num_iterations = 3 + layer_sets = [ + self.get_layers((2**x)*self.n_layers) + for x in range(num_iterations) + ] + glass_sets = [ + Mobject(*[ + Mobject( + *layer_sets[x][(2**x)*index:(2**x)*(index+1)] + ) + for index in range(self.n_layers) + ]).ingest_submobjects() + for x in range(num_iterations) + ] + glass_sets.append(self.get_continuous_glass()) + for glass_set in glass_sets: + glass_set.sort_points(lambda p : p[1]) + curr_set = glass_sets[0] + self.add(curr_set) + for layer_set in glass_sets[1:]: + self.dither() + self.play(Transform(curr_set, layer_set)) + self.dither() + + + +class ShowLightAndSlidingObject(MultilayeredScene, TryManyPaths, PhotonScene): + CONFIG = { + "show_time" : False, + "dither_and_add" : False, + "RectClass" : FilledRectangle + } + def construct(self): + glass = self.get_continuous_glass() + self.play(ApplyMethod(glass.fade, 0.8)) + self.freeze_background() + + paths = self.get_paths() + for path in paths: + if path.get_height() > self.total_glass_height: + path.stretch(0.7, 1) + path.shift(self.top - path.get_top()) + path.rgbs[:,2] = 0 + loop = paths.pop(1) ##Bad! + randy = Randolph() + randy.scale(RANDY_SCALE_VAL) + randy.shift(-randy.get_bottom()) + photon_run = self.photon_run_along_path( + loop, + rate_func = lambda t : smooth(1.2*t, 2), + run_time = 4.1 + ) + text = self.get_text().to_edge(UP, buff = 0.2) + + self.play(ShowCreation(loop)) + self.dither() + self.play(photon_run) + self.remove(photon_run.mobject) + randy = self.slide(randy, loop) + self.add(randy) + self.dither() + self.remove(randy) + self.play(ShimmerIn(text)) + for path in paths: + self.play(Transform( + loop, path, + path_func = path_along_arc(np.pi/2), + run_time = 2 + )) + + +class ContinuouslyObeyingSnellsLaw(MultilayeredScene): + CONFIG = { + "arc_radius" : 0.5, + "RectClass" : FilledRectangle + } + def construct(self): + glass = self.get_continuous_glass() + self.add(glass) + self.freeze_background() + + cycloid = Cycloid(end_theta = np.pi) + cycloid.highlight(YELLOW) + chopped_cycloid = cycloid.copy() + n = cycloid.get_num_points() + chopped_cycloid.filter_out(lambda p : p[1] > 1 and p[0] < 0) + chopped_cycloid.reverse_points() + + + self.play(ShowCreation(cycloid)) + ref_mob = self.snells_law_at_every_point(cycloid, chopped_cycloid) + self.show_equation(chopped_cycloid, ref_mob) + + def snells_law_at_every_point(self, cycloid, chopped_cycloid): + square = Square(side_length = 0.2, color = WHITE) + words = TextMobject(["Snell's law ", "everywhere"]) + snells, rest = words.split() + colon = TextMobject(":") + words.next_to(square) + words.shift(0.3*UP) + combo = Mobject(square, words) + combo.get_center = lambda : square.get_center() + new_snells = snells.copy().center().to_edge(UP, buff = 1.5) + colon.next_to(new_snells) + colon.shift(0.05*DOWN) + + self.play(MoveAlongPath( + combo, cycloid, + run_time = 5 + )) + self.play(MoveAlongPath( + combo, chopped_cycloid, + run_time = 4 + )) + dot = Dot(combo.get_center()) + self.play(Transform(square, dot)) + self.play( + Transform(snells, new_snells), + Transform(rest, colon) + ) + self.dither() + return colon + + def get_marks(self, point1, point2): + vert_line = Line(2*DOWN, 2*UP) + tangent_line = vert_line.copy() + theta = TexMobject("\\theta") + theta.scale(0.5) + angle = angle_of_vector(point1 - point2) + tangent_line.rotate( + angle - tangent_line.get_angle() + ) + angle_from_vert = angle - np.pi/2 + for mob in vert_line, tangent_line: + mob.shift(point1 - mob.get_center()) + arc = Arc(angle_from_vert, start_angle = np.pi/2) + arc.scale(self.arc_radius) + arc.shift(point1) + vect_angle = angle_from_vert/2 + np.pi/2 + vect = rotate_vector(RIGHT, vect_angle) + theta.center() + theta.shift(point1) + theta.shift(1.5*self.arc_radius*vect) + return arc, theta, vert_line, tangent_line + + + def show_equation(self, chopped_cycloid, ref_mob): + point2, point1 = chopped_cycloid.points[-2:] + arc, theta, vert_line, tangent_line = self.get_marks( + point1, point2 + ) + equation = TexMobject([ + "\\sin(\\theta)", + "\\over \\sqrt{y}", + ]) + sin, sqrt_y = equation.split() + equation.next_to(ref_mob) + const = TexMobject(" = \\text{constant}") + const.next_to(equation) + ceil_point = np.array(point1) + ceil_point[1] = self.top[1] + brace = Brace( + Mobject(Point(point1), Point(ceil_point)), + RIGHT + ) + y_mob = TexMobject("y").next_to(brace) + + self.play( + GrowFromCenter(sin), + ShowCreation(arc), + GrowFromCenter(theta) + ) + self.play(ShowCreation(vert_line)) + self.play(ShowCreation(tangent_line)) + self.dither() + self.play( + GrowFromCenter(sqrt_y), + GrowFromCenter(brace), + GrowFromCenter(y_mob) + ) + self.dither() + self.play(Transform( + Point(const.get_left()), const + )) + self.dither() + + + + + + + + + + + + + + + + + + + diff --git a/old_projects/brachistochrone/wordplay.py b/old_projects/brachistochrone/wordplay.py new file mode 100644 index 00000000..45d6f593 --- /dev/null +++ b/old_projects/brachistochrone/wordplay.py @@ -0,0 +1,572 @@ +import numpy as np +import itertools as it +import os + +from helpers import * + +from mobject.tex_mobject import TexMobject, TextMobject, Brace +from mobject import Mobject +from mobject.image_mobject import \ + ImageMobject, MobjectFromPixelArray +from topics.three_dimensions import Stars + +from animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import TurnInsideOut, Vibrate +from topics.geometry import * +from topics.characters import Randolph, Mathematician +from topics.functions import ParametricFunction, FunctionGraph +from topics.number_line import NumberPlane +from mobject.region import Region, region_from_polygon_vertices +from scene import Scene +from generate_logo import LogoGeneration +from brachistochrone.drawing_images import sort_by_color + +class Intro(Scene): + def construct(self): + logo = ImageMobject("LogoGeneration", invert = False) + name_mob = TextMobject("3Blue1Brown").center() + name_mob.highlight("grey") + name_mob.shift(2*DOWN) + self.add(name_mob, logo) + + new_text = TextMobject(["with ", "Steven Strogatz"]) + new_text.next_to(name_mob, DOWN) + self.play(*[ + ShimmerIn(part) + for part in new_text.split() + ]) + self.dither() + with_word, steve = new_text.split() + steve_copy = steve.copy().center().to_edge(UP) + # logo.sort_points(lambda p : -np.linalg.norm(p)) + sort_by_color(logo) + self.play( + Transform(steve, steve_copy), + DelayByOrder(Transform(logo, Point())), + FadeOut(with_word), + FadeOut(name_mob), + run_time = 3 + ) + + +class IntroduceSteve(Scene): + def construct(self): + name = TextMobject("Steven Strogatz") + name.to_edge(UP) + contributions = TextMobject("Frequent Contributions") + contributions.scale(0.5).to_edge(RIGHT).shift(2*UP) + books_word = TextMobject("Books") + books_word.scale(0.5).to_edge(LEFT).shift(2*UP) + radio_lab, sci_fri, cornell, book2, book3, book4 = [ + ImageMobject(filename, invert = False, filter_color = WHITE) + for filename in [ + "radio_lab", + "science_friday", + "cornell", + "strogatz_book2", + "strogatz_book3", + "strogatz_book4", + ] + ] + book1 = ImageMobject("strogatz_book1", invert = False) + nyt = ImageMobject("new_york_times") + logos = [radio_lab, nyt, sci_fri] + books = [book1, book2, book3, book4] + + sample_size = Square(side_length = 2) + last = contributions + for image in logos: + image.replace(sample_size) + image.next_to(last, DOWN) + last = image + sci_fri.scale_in_place(0.9) + shift_val = 0 + sample_size.scale(0.75) + for book in books: + book.replace(sample_size) + book.next_to(books_word, DOWN) + book.shift(shift_val*(RIGHT+DOWN)) + shift_val += 0.5 + sample_size.scale(2) + cornell.replace(sample_size) + cornell.next_to(name, DOWN) + + self.add(name) + self.play(FadeIn(cornell)) + self.play(ShimmerIn(books_word)) + for book in books: + book.shift(5*LEFT) + self.play(ApplyMethod(book.shift, 5*RIGHT)) + self.play(ShimmerIn(contributions)) + for logo in logos: + self.play(FadeIn(logo)) + self.dither() + +class ShowTweets(Scene): + def construct(self): + tweets = [ + ImageMobject("tweet%d"%x, invert = False) + for x in range(1, 4) + ] + for tweet in tweets: + tweet.scale(0.4) + tweets[0].to_corner(UP+LEFT) + tweets[1].next_to(tweets[0], RIGHT, aligned_edge = UP) + tweets[2].next_to(tweets[1], DOWN) + + self.play(GrowFromCenter(tweets[0])) + for x in 1, 2: + self.play( + Transform(Point(tweets[x-1].get_center()), tweets[x]), + Animation(tweets[x-1]) + ) + self.dither() + +class LetsBeHonest(Scene): + def construct(self): + self.play(ShimmerIn(TextMobject(""" + Let's be honest about who benefits + from this collaboration... + """))) + self.dither() + + +class WhatIsTheBrachistochrone(Scene): + def construct(self): + self.play(ShimmerIn(TextMobject(""" + So \\dots what is the Brachistochrone? + """))) + self.dither() + + +class DisectBrachistochroneWord(Scene): + def construct(self): + word = TextMobject(["Bra", "chis", "to", "chrone"]) + original_word = word.copy() + dots = [] + for part in word.split(): + if dots: + part.next_to(dots[-1], buff = 0.06) + dot = TexMobject("\\cdot") + dot.next_to(part, buff = 0.06) + dots.append(dot) + dots = Mobject(*dots[:-1]) + dots.shift(0.1*DOWN) + Mobject(word, dots).center() + overbrace1 = Brace(Mobject(*word.split()[:-1]), UP) + overbrace2 = Brace(word.split()[-1], UP) + shortest = TextMobject("Shortest") + shortest.next_to(overbrace1, UP) + shortest.highlight(YELLOW) + time = TextMobject("Time") + time.next_to(overbrace2, UP) + time.highlight(YELLOW) + chrono_example = TextMobject(""" + As in ``Chronological'' \\\\ + or ``Synchronize'' + """) + chrono_example.scale(0.5) + chrono_example.to_edge(RIGHT) + chrono_example.shift(2*UP) + chrono_example.highlight(BLUE_D) + chrono_arrow = Arrow( + word.get_right(), + chrono_example.get_bottom(), + color = BLUE_D + ) + brachy_example = TextMobject("As in . . . brachydactyly?") + brachy_example.scale(0.5) + brachy_example.to_edge(LEFT) + brachy_example.shift(2*DOWN) + brachy_example.highlight(GREEN) + brachy_arrow = Arrow( + word.get_left(), + brachy_example.get_top(), + color = GREEN + ) + + pronunciation = TextMobject(["/br", "e", "kist","e","kr$\\bar{o}$n/"]) + pronunciation.split()[1].rotate_in_place(np.pi) + pronunciation.split()[3].rotate_in_place(np.pi) + pronunciation.scale(0.7) + pronunciation.shift(DOWN) + + latin = TextMobject(list("Latin")) + greek = TextMobject(list("Greek")) + for mob in latin, greek: + mob.to_edge(LEFT) + question_mark = TextMobject("?").next_to(greek, buff = 0.1) + stars = Stars().highlight(BLACK) + stars.scale(0.5).shift(question_mark.get_center()) + + self.play(Transform(original_word, word), ShowCreation(dots)) + self.play(ShimmerIn(pronunciation)) + self.dither() + self.play( + GrowFromCenter(overbrace1), + GrowFromCenter(overbrace2) + ) + self.dither() + self.play(ShimmerIn(latin)) + self.play(FadeIn(question_mark)) + self.play(Transform( + latin, greek, + path_func = counterclockwise_path() + )) + self.dither() + self.play(Transform(question_mark, stars)) + self.remove(stars) + self.dither() + self.play(ShimmerIn(shortest)) + self.play(ShimmerIn(time)) + for ex, ar in [(chrono_example, chrono_arrow), (brachy_example, brachy_arrow)]: + self.play( + ShowCreation(ar), + ShimmerIn(ex) + ) + self.dither() + +class OneSolutionTwoInsights(Scene): + def construct(self): + one_solution = TextMobject(["One ", "solution"]) + two_insights = TextMobject(["Two ", " insights"]) + two, insights = two_insights.split() + johann = ImageMobject("Johann_Bernoulli2", invert = False) + mark = ImageMobject("Mark_Levi", invert = False) + for mob in johann, mark: + mob.scale(0.4) + johann.next_to(insights, LEFT) + mark.next_to(johann, RIGHT) + name = TextMobject("Mark Levi").to_edge(UP) + + self.play(*map(ShimmerIn, one_solution.split())) + self.dither() + for pair in zip(one_solution.split(), two_insights.split()): + self.play(Transform(*pair, path_func = path_along_arc(np.pi))) + self.dither() + self.clear() + self.add(two, insights) + for word, man in [(two, johann), (insights, mark)]: + self.play( + Transform(word, Point(word.get_left())), + GrowFromCenter(man) + ) + self.dither() + self.clear() + self.play(ApplyMethod(mark.center)) + self.play(ShimmerIn(name)) + self.dither() + +class CircleOfIdeas(Scene): + def construct(self): + words = map(TextMobject, [ + "optics", "calculus", "mechanics", "geometry", "history" + ]) + words[0].highlight(YELLOW) + words[1].highlight(BLUE_D) + words[2].highlight(GREY) + words[3].highlight(GREEN) + words[4].highlight(MAROON) + brachistochrone = TextMobject("Brachistochrone") + displayed_words = [] + for word in words: + anims = self.get_spinning_anims(displayed_words) + word.shift(3*RIGHT) + point = Point() + anims.append(Transform(point, word)) + self.play(*anims) + self.remove(point) + self.add(word) + displayed_words.append(word) + self.play(*self.get_spinning_anims(displayed_words)) + self.play(*[ + Transform( + word, word.copy().highlight(BLACK).center().scale(0.1), + path_func = path_along_arc(np.pi), + rate_func = None, + run_time = 2 + ) + for word in displayed_words + ]+[ + GrowFromCenter(brachistochrone) + ]) + self.dither() + + def get_spinning_anims(self, words, angle = np.pi/6): + anims = [] + for word in words: + old_center = word.get_center() + new_center = rotate_vector(old_center, angle) + vect = new_center-old_center + anims.append(ApplyMethod( + word.shift, vect, + path_func = path_along_arc(angle), + rate_func = None + )) + return anims + + +class FermatsPrincipleStatement(Scene): + def construct(self): + words = TextMobject([ + "Fermat's principle:", + """ + If a beam of light travels + from point $A$ to $B$, it does so along the + fastest path possible. + """ + ]) + words.split()[0].highlight(BLUE) + everything = MobjectFromRegion(Region()) + everything.scale(0.9) + angles = np.apply_along_axis( + angle_of_vector, 1, everything.points + ) + norms = np.apply_along_axis( + np.linalg.norm, 1, everything.points + ) + norms -= np.min(norms) + norms /= np.max(norms) + alphas = 0.25 + 0.75 * norms * (1 + np.sin(12*angles))/2 + everything.rgbs = alphas.repeat(3).reshape((len(alphas), 3)) + + Mobject(everything, words).show() + + everything.sort_points(np.linalg.norm) + self.add(words) + self.play( + DelayByOrder(FadeIn(everything, run_time = 3)), + Animation(words) + ) + self.play( + ApplyMethod(everything.highlight, WHITE), + ) + self.dither() + +class VideoProgression(Scene): + def construct(self): + spacing = 2*UP + brachy, optics, light_in_two, snells, multi = words = [ + TextMobject(text) + for text in [ + "Brachistochrone", + "Optics", + "Light in two media", + "Snell's Law", + "Multilayered glass", + ] + ] + for mob in light_in_two, snells: + mob.shift(-spacing) + arrow1 = Arrow(brachy, optics) + arrow2 = Arrow(optics, snells) + point = Point(DOWN) + + self.play(ShimmerIn(brachy)) + self.dither() + self.play( + ApplyMethod(brachy.shift, spacing), + Transform(point, optics) + ) + optics = point + arrow1 = Arrow(optics, brachy) + self.play(ShowCreation(arrow1)) + self.dither() + arrow2 = Arrow(light_in_two, optics) + self.play( + ShowCreation(arrow2), + ShimmerIn(light_in_two) + ) + self.dither() + self.play( + FadeOut(light_in_two), + GrowFromCenter(snells), + DelayByOrder( + ApplyMethod(arrow2.highlight, BLUE_D) + ) + ) + self.dither() + self.play( + FadeOut(optics), + GrowFromCenter(multi), + DelayByOrder( + ApplyMethod(arrow1.highlight, BLUE_D) + ) + ) + self.dither() + + + + + +class BalanceCompetingFactors(Scene): + args_list = [ + ("Short", "Steep"), + ("Minimal time \\\\ in water", "Short path") + ] + + @staticmethod + def args_to_string(*words): + return "".join([word.split(" ")[0] for word in words]) + + def construct(self, *words): + factor1, factor2 = [ + TextMobject("Factor %d"%x).highlight(c) + for x, c in [ + (1, RED_D), + (2, BLUE_D) + ] + ] + real_factor1, real_factor2 = map(TextMobject, words) + for word in factor1, factor2, real_factor1, real_factor2: + word.shift(0.2*UP-word.get_bottom()) + for f1 in factor1, real_factor1: + f1.highlight(RED_D) + f1.shift(2*LEFT) + for f2 in factor2, real_factor2: + f2.highlight(BLUE_D) + f2.shift(2*RIGHT) + line = Line( + factor1.get_left(), + factor2.get_right() + ) + line.center() + self.balancers = Mobject(factor1, factor2, line) + self.hidden_balancers = Mobject(real_factor1, real_factor2) + + triangle = Polygon(RIGHT, np.sqrt(3)*UP, LEFT) + triangle.next_to(line, DOWN, buff = 0) + + self.add(triangle, self.balancers) + self.rotate(1) + self.rotate(-2) + self.dither() + self.play(Transform( + factor1, real_factor1, + path_func = path_along_arc(np.pi/4) + )) + self.rotate(2) + self.dither() + self.play(Transform( + factor2, real_factor2, + path_func = path_along_arc(np.pi/4) + )) + self.rotate(-2) + self.dither() + self.rotate(1) + + def rotate(self, factor): + angle = np.pi/11 + self.play(Rotate( + self.balancers, + factor*angle, + run_time = abs(factor) + )) + self.hidden_balancers.rotate(factor*angle) + + + + +class Challenge(Scene): + def construct(self): + self.add(TextMobject(""" + Can you find a new solution to the + Brachistochrone problem by finding + an intuitive reason that time-minimizing + curves look like straight lines in + $t$-$\\theta$ space? + """)) + self.dither() + + + +class Section1(Scene): + def construct(self): + self.add(TextMobject("Section 1: Johann Bernoulli's insight")) + self.dither() + +class Section2(Scene): + def construct(self): + self.add(TextMobject( + "Section 2: Mark Levi's insight, and a challenge", + size = "\\large" + )) + self.dither() + + + +class NarratorInterjection(Scene): + def construct(self): + words1 = TexMobject("<\\text{Narrator interjection}>") + words2 = TexMobject("<\\!/\\text{Narrator interjection}>") + self.add(words1) + self.dither() + self.clear() + self.add(words2) + self.dither() + + +class ThisCouldBeTheEnd(Scene): + def construct(self): + words = TextMobject([ + "This could be the end\\dots", + "but\\dots" + ]) + for part in words.split(): + self.play(ShimmerIn(part)) + self.dither() + + +class MyOwnChallenge(Scene): + def construct(self): + self.add(TextMobject("My own challenge:")) + self.dither() + + +class WarmupChallenge(Scene): + def construct(self): + self.add(TextMobject("\\large Warm-up challenge: Confirm this for yourself")) + self.dither() + +class FindAnotherSolution(Scene): + def construct(self): + self.add(TextMobject("Find another brachistochrone solution\\dots")) + self.dither() + + +class ProofOfSnellsLaw(Scene): + def construct(self): + self.add(TextMobject("Proof of Snell's law:")) + self.dither() + + +class CondensedVersion(Scene): + def construct(self): + snells = TextMobject("Snell's") + snells.shift(-snells.get_left()) + snells.to_edge(UP) + for vect in [RIGHT, RIGHT, LEFT, DOWN, DOWN, DOWN]: + snells.add(snells.copy().next_to(snells, vect)) + snells.ingest_submobjects() + snells.show() + condensed = TextMobject("condensed") + + self.add(snells) + self.dither() + self.play(DelayByOrder( + Transform(snells, condensed, run_time = 2) + )) + self.dither() + + + + + + + + + + + diff --git a/old_projects/hilbert/__init__.py b/old_projects/hilbert/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/old_projects/hilbert/fractal_porn.py b/old_projects/hilbert/fractal_porn.py new file mode 100644 index 00000000..eda5f514 --- /dev/null +++ b/old_projects/hilbert/fractal_porn.py @@ -0,0 +1,347 @@ +from mobject import Mobject, Point, Mobject1D +from scene import Scene +from animation.transform import \ + Transform, ShimmerIn, FadeIn, FadeOut, ApplyMethod +from animation.simple_animations import \ + ShowCreation, DelayByOrder +from topics.geometry import Line, Arc, Arrow +from mobject.tex_mobject import TexMobject, TextMobject + +from helpers import * +from hilbert.curves import * + +class Intro(TransformOverIncreasingOrders): + @staticmethod + def args_to_string(*args): + return "" + + @staticmethod + def string_to_args(string): + raise Exception("string_to_args Not Implemented!") + + def construct(self): + words1 = TextMobject( + "If you watched my video about Hilbert's space-filling curve\\dots" + ) + words2 = TextMobject( + "\\dots you might be curious to see what a few other space-filling curves look like." + ) + words2.scale(0.8) + for words in words1, words2: + words.to_edge(UP, buff = 0.2) + + self.setup(HilbertCurve) + self.play(ShimmerIn(words1)) + for x in range(4): + self.increase_order() + self.remove(words1) + self.increase_order( + ShimmerIn(words2) + ) + for x in range(4): + self.increase_order() + + + +class BringInPeano(Intro): + def construct(self): + words1 = TextMobject(""" + For each one, see if you can figure out what + the pattern of construction is. + """) + words2 = TextMobject(""" + This one is the Peano curve. + """) + words3 = TextMobject(""" + It is the original space-filling curve. + """) + self.setup(PeanoCurve) + self.play(ShimmerIn(words1)) + self.dither(5) + self.remove(words1) + self.add(words2.to_edge(UP)) + for x in range(3): + self.increase_order() + self.remove(words2) + self.increase_order(ShimmerIn(words3.to_edge(UP))) + for x in range(2): + self.increase_order() + + +class FillOtherShapes(Intro): + def construct(self): + words1 = TextMobject(""" + But of course, there's no reason we should limit + ourselves to filling in squares. + """) + words2 = TextMobject(""" + Here's a simple triangle-filling curve I defined + in a style reflective of a Hilbert curve. + """) + words1.to_edge(UP) + words2.scale(0.8).to_edge(UP, buff = 0.2) + + self.setup(TriangleFillingCurve) + self.play(ShimmerIn(words1)) + for x in range(3): + self.increase_order() + self.remove(words1) + self.add(words2) + for x in range(5): + self.increase_order() + +class SmallerFlowSnake(FlowSnake): + CONFIG = { + "radius" : 4 + } + +class MostDelightfulName(Intro): + def construct(self): + words1 = TextMobject(""" + This one has the most delightful name, + thanks to mathematician/programmer Bill Gosper: + """) + words2 = TextMobject("``Flow Snake''") + words3 = TextMobject(""" + What makes this one particularly interesting + is that the boundary itself is a fractal. + """) + for words in words1, words2, words3: + words.to_edge(UP) + + self.setup(SmallerFlowSnake) + self.play(ShimmerIn(words1)) + for x in range(3): + self.increase_order() + self.remove(words1) + self.add(words2) + for x in range(3): + self.increase_order() + self.remove(words2) + self.play(ShimmerIn(words3)) + + + +class SurpriseFractal(Intro): + def construct(self): + words = TextMobject(""" + It might come as a surprise how some well-known + fractals can be described with curves. + """) + words.to_edge(UP) + + self.setup(Sierpinski) + self.add(TextMobject("Speaking of other fractals\\dots")) + self.dither(3) + self.clear() + self.play(ShimmerIn(words)) + for x in range(9): + self.increase_order() + + +class IntroduceKoch(Intro): + def construct(self): + words = map(TextMobject, [ + "This is another famous fractal.", + "The ``Koch Snowflake''", + "Let's finish things off by seeing how to turn \ + this into a space-filling curve" + ]) + for text in words: + text.to_edge(UP) + + self.setup(KochCurve) + self.add(words[0]) + for x in range(3): + self.increase_order() + self.remove(words[0]) + self.add(words[1]) + for x in range(4): + self.increase_order() + self.remove(words[1]) + self.add(words[2]) + self.dither(6) + +class StraightKoch(KochCurve): + CONFIG = { + "axiom" : "A" + } + +class SharperKoch(StraightKoch): + CONFIG = { + "angle" : 0.9*np.pi/2, + } + +class DullerKoch(StraightKoch): + CONFIG = { + "angle" : np.pi/6, + } + +class SpaceFillingKoch(StraightKoch): + CONFIG = { + "angle" : np.pi/2, + } + + + +class FromKochToSpaceFilling(Scene): + def construct(self): + self.max_order = 7 + + self.revisit_koch() + self.show_angles() + self.show_change_side_by_side() + + + def revisit_koch(self): + words = map(TextMobject, [ + "First, look at how one section of this curve is made.", + "This pattern of four lines is the ``seed''", + "With each iteration, every straight line is \ + replaced with an appropriately small copy of the seed", + ]) + for text in words: + text.to_edge(UP) + + self.add(words[0]) + curve = StraightKoch(order = self.max_order) + self.play(Transform( + curve, + StraightKoch(order = 1), + run_time = 5 + )) + self.remove(words[0]) + self.add(words[1]) + self.dither(4) + self.remove(words[1]) + self.add(words[2]) + self.dither(3) + for order in range(2, self.max_order): + self.play(Transform( + curve, + StraightKoch(order = order) + )) + if order == 2: + self.dither(2) + elif order == 3: + self.dither() + self.clear() + + + + def show_angles(self): + words = TextMobject(""" + Let's see what happens as we change + the angle in this seed + """) + words.to_edge(UP) + koch, sharper_koch, duller_koch = curves = [ + CurveClass(order = 1) + for CurveClass in StraightKoch, SharperKoch, DullerKoch + ] + arcs = [ + Arc( + 2*(np.pi/2 - curve.angle), + radius = r, + start_angle = np.pi+curve.angle + ).shift(curve.points[curve.get_num_points()/2]) + for curve, r in zip(curves, [0.6, 0.7, 0.4]) + ] + theta = TexMobject("\\theta") + theta.shift(arcs[0].get_center()+2.5*DOWN) + arrow = Arrow(theta, arcs[0]) + + self.add(words, koch) + self.play(ShowCreation(arcs[0])) + self.play( + ShowCreation(arrow), + ShimmerIn(theta) + ) + self.dither(2) + self.remove(theta, arrow) + self.play( + Transform(koch, duller_koch), + Transform(arcs[0], arcs[2]), + ) + self.play( + Transform(koch, sharper_koch), + Transform(arcs[0], arcs[1]), + ) + self.clear() + + def show_change_side_by_side(self): + + seed = TextMobject("Seed") + seed.shift(3*LEFT+2*DOWN) + fractal = TextMobject("Fractal") + fractal.shift(3*RIGHT+2*DOWN) + words = map(TextMobject, [ + "A sharper angle results in a richer curve", + "A more obtuse angle gives a sparser curve", + "And as the angle approaches 0\\dots", + "We have a new space-filling curve." + ]) + for text in words: + text.to_edge(UP) + sharper, duller, space_filling = [ + CurveClass(order = 1).shift(3*LEFT) + for CurveClass in SharperKoch, DullerKoch, SpaceFillingKoch + ] + shaper_f, duller_f, space_filling_f = [ + CurveClass(order = self.max_order).shift(3*RIGHT) + for CurveClass in SharperKoch, DullerKoch, SpaceFillingKoch + ] + + self.add(words[0]) + left_curve = SharperKoch(order = 1) + right_curve = SharperKoch(order = 1) + self.play( + Transform(left_curve, sharper), + ApplyMethod(right_curve.shift, 3*RIGHT), + ) + self.play( + Transform( + right_curve, + SharperKoch(order = 2).shift(3*RIGHT) + ), + ShimmerIn(seed), + ShimmerIn(fractal) + ) + for order in range(3, self.max_order): + self.play(Transform( + right_curve, + SharperKoch(order = order).shift(3*RIGHT) + )) + self.remove(words[0]) + self.add(words[1]) + kwargs = { + "run_time" : 4, + } + self.play( + Transform(left_curve, duller, **kwargs), + Transform(right_curve, duller_f, **kwargs) + ) + self.dither() + kwargs["run_time"] = 7 + kwargs["rate_func"] = None + self.remove(words[1]) + self.add(words[2]) + self.play( + Transform(left_curve, space_filling, **kwargs), + Transform(right_curve, space_filling_f, **kwargs) + ) + self.remove(words[2]) + self.add(words[3]) + self.dither() + + + + + + + + + + + + diff --git a/old_projects/hilbert/section1.py b/old_projects/hilbert/section1.py new file mode 100644 index 00000000..39fcd7bb --- /dev/null +++ b/old_projects/hilbert/section1.py @@ -0,0 +1,1089 @@ +from mobject import Mobject, Point +from mobject.tex_mobject import \ + TexMobject, TextMobject, Brace +from mobject.image_mobject import \ + ImageMobject, MobjectFromRegion + +from scene import Scene + +from animation import Animation +from animation.transform import \ + Transform, CounterclockwiseTransform, ApplyMethod,\ + GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction,\ + TransformAnimations, ShimmerIn, FadeOut, FadeIn +from animation.simple_animations import \ + ShowCreation, DelayByOrder +from animation.playground import Vibrate + +from topics.geometry import \ + Line, Dot, Arrow, Grid, Square, Point +from topics.characters import \ + ThoughtBubble, SpeechBubble, Mathematician, Randolph +from topics.number_line import UnitInterval +from topics.three_dimensions import Stars + +from mobject.region import region_from_polygon_vertices + +import displayer as disp + +from hilbert.curves import \ + TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \ + SnakeCurve + + +from helpers import * + + + +def get_grid(): + return Grid(64, 64) + +def get_freq_line(): + return UnitInterval().shift(2*DOWN) ##Change? + +def get_mathy_and_bubble(): + mathy = Mathematician() + mathy.to_edge(DOWN).shift(4*LEFT) + bubble = SpeechBubble(initial_width = 8) + bubble.pin_to(mathy) + return mathy, bubble + +class AboutSpaceFillingCurves(TransformOverIncreasingOrders): + @staticmethod + def args_to_string(): + return "" + + @staticmethod + def string_to_args(arg_str): + return () + + def construct(self): + self.bubble = ThoughtBubble().ingest_submobjects() + self.bubble.scale(1.5) + + TransformOverIncreasingOrders.construct(self, FlowSnake, 7) + self.play(Transform(self.curve, self.bubble)) + self.show_infinite_objects() + self.pose_question() + self.dither() + + def show_infinite_objects(self): + sigma, summand, equals, result = TexMobject([ + "\\sum_{n = 1}^{\\infty}", + "\\dfrac{1}{n^2}", + "=", + "\\dfrac{\pi^2}{6}" + ]).split() + alt_summand = TexMobject("n").replace(summand) + alt_result = TexMobject("-\\dfrac{1}{12}").replace(result) + + rationals, other_equals, naturals = TexMobject([ + "|\\mathds{Q}|", + "=", + "|\\mathds{N}|" + ]).scale(2).split() + infinity = TexMobject("\\infty").scale(2) + local_mobjects = filter( + lambda m : isinstance(m, Mobject), + locals().values(), + ) + for mob in local_mobjects: + mob.sort_points(np.linalg.norm) + + self.play(ShimmerIn(infinity)) + self.dither() + self.play( + ShimmerIn(summand), + ShimmerIn(equals), + ShimmerIn(result), + DelayByOrder(Transform(infinity, sigma)) + ) + self.dither() + self.play( + Transform(summand, alt_summand), + Transform(result, alt_result), + ) + self.dither() + self.remove(infinity) + self.play(*[ + CounterclockwiseTransform( + Mobject(summand, equals, result, sigma), + Mobject(rationals, other_equals, naturals) + ) + ]) + self.dither() + self.clear() + self.add(self.bubble) + + def pose_question(self): + infinity, rightarrow, N = TexMobject([ + "\\infty", "\\rightarrow", "N" + ]).scale(2).split() + question_mark = TextMobject("?").scale(2) + + self.add(question_mark) + self.dither() + self.play(*[ + ShimmerIn(mob) + for mob in infinity, rightarrow, N + ] + [ + ApplyMethod(question_mark.next_to, rightarrow, UP), + ]) + self.dither() + + + +class PostponePhilosophizing(Scene): + def construct(self): + abstract, arrow, concrete = TextMobject([ + "Abstract", " $\\rightarrow$ ", "Concrete" + ]).scale(2).split() + + self.add(abstract, arrow, concrete) + self.dither() + self.play(*[ + ApplyMethod( + word1.replace, word2, + path_func = path_along_arc(np.pi/2) + ) + for word1, word2 in it.permutations([abstract, concrete]) + ]) + self.dither() + + +class GrowHilbertWithName(Scene): + def construct(self): + curve = HilbertCurve(order = 1) + words = TextMobject("``Hilbert Curve''") + words.to_edge(UP, buff = 0.2) + self.play( + ShimmerIn(words), + Transform(curve, HilbertCurve(order = 2)), + run_time = 2 + ) + for n in range(3, 8): + self.play( + Transform(curve, HilbertCurve(order = n)), + run_time = 5. /n + ) + + +class SectionOne(Scene): + def construct(self): + self.add(TextMobject("Section 1: Seeing with your ears")) + self.dither() + +class WriteSomeSoftware(Scene): + pass #Done viea screen capture, written here for organization + + + +class ImageToSound(Scene): + def construct(self): + string = Vibrate(color = BLUE_D, run_time = 5) + picture = ImageMobject("lion", invert = False) + picture.scale(0.8) + picture_copy = picture.copy() + picture.sort_points(np.linalg.norm) + string.mobject.sort_points(lambda p : -np.linalg.norm(p)) + + self.add(picture) + self.dither() + self.play(Transform( + picture, string.mobject, + run_time = 3, + rate_func = rush_into + )) + self.remove(picture) + self.play(string) + + for mob in picture_copy, string.mobject: + mob.sort_points(lambda p : np.linalg.norm(p)%1) + + self.play(Transform( + string.mobject, picture_copy, + run_time = 5, + rate_func = rush_from + )) + +class LinksInDescription(Scene): + def construct(self): + text = TextMobject(""" + See links in the description for more on + sight via sound. + """) + self.play(ShimmerIn(text)) + self.play(ShowCreation(Arrow(text, 3*DOWN))) + self.dither(2) + + +class ImageDataIsTwoDimensional(Scene): + def construct(self): + image = ImageMobject("lion", invert = False) + image.scale(0.5) + image.shift(2*LEFT) + + self.add(image) + for vect, num in zip([DOWN, RIGHT], [1, 2]): + brace = Brace(image, vect) + words_mob = TextMobject("Dimension %d"%num) + words_mob.next_to(image, vect, buff = 1) + self.play( + Transform(Point(brace.get_center()), brace), + ShimmerIn(words_mob), + run_time = 2 + ) + self.dither() + + +class SoundDataIsOneDimensional(Scene): + def construct(self): + overtones = 5 + floor = 2*DOWN + main_string = Vibrate(color = BLUE_D) + component_strings = [ + Vibrate( + num_periods = k+1, + overtones = 1, + color = color, + center = 2*DOWN + UP*k + ) + for k, color in zip( + range(overtones), + Color(BLUE_E).range_to(WHITE, overtones) + ) + ] + dots = [ + Dot( + string.mobject.get_center(), + color = string.mobject.get_color() + ) + for string in component_strings + ] + + freq_line = get_freq_line() + freq_line.shift(floor) + freq_line.sort_points(np.linalg.norm) + brace = Brace(freq_line, UP) + words = TextMobject("Range of frequency values") + words.next_to(brace, UP) + + + self.play(*[ + TransformAnimations( + main_string.copy(), + string, + run_time = 5 + ) + for string in component_strings + ]) + self.clear() + self.play(*[ + TransformAnimations( + string, + Animation(dot) + ) + for string, dot in zip(component_strings, dots) + ]) + self.clear() + self.play( + ShowCreation(freq_line), + GrowFromCenter(brace), + ShimmerIn(words), + *[ + Transform( + dot, + dot.copy().scale(2).rotate(-np.pi/2).shift(floor), + path_func = path_along_arc(np.pi/3) + ) + for dot in dots + ] + ) + self.dither(0.5) + +class GridOfPixels(Scene): + def construct(self): + low_res = ImageMobject("low_resolution_lion", invert = False) + high_res = ImageMobject("Lion", invert = False) + grid = get_grid().scale(0.8) + for mob in low_res, high_res: + mob.replace(grid, stretch = True) + side_brace = Brace(low_res, LEFT) + top_brace = Brace(low_res, UP) + top_words = TextMobject("256 Px", size = "\\normal") + side_words = top_words.copy().rotate(np.pi/2) + top_words.next_to(top_brace, UP) + side_words.next_to(side_brace, LEFT) + + self.add(high_res) + self.dither() + self.play(DelayByOrder(Transform(high_res, low_res))) + self.dither() + self.play( + GrowFromCenter(top_brace), + GrowFromCenter(side_brace), + ShimmerIn(top_words), + ShimmerIn(side_words) + ) + self.dither() + for mob in grid, high_res: + mob.sort_points(np.linalg.norm) + self.play(DelayByOrder(Transform(high_res, grid))) + self.dither() + + +class ShowFrequencySpace(Scene): + def construct(self): + freq_line = get_freq_line() + + self.add(freq_line) + self.dither() + for tex, vect in zip(["20 Hz", "20{,}000 Hz"], [LEFT, RIGHT]): + tex_mob = TextMobject(tex) + tex_mob.to_edge(vect) + tex_mob.shift(UP) + arrow = Arrow(tex_mob, freq_line.get_edge_center(vect)) + self.play( + ShimmerIn(tex_mob), + ShowCreation(arrow) + ) + self.dither() + + + +class AssociatePixelWithFrequency(Scene): + def construct(self): + big_grid_dim = 20. + small_grid_dim = 6. + big_grid = Grid(64, 64, height = big_grid_dim, width = big_grid_dim) + big_grid.to_corner(UP+RIGHT, buff = 2) + small_grid = big_grid.copy() + small_grid.scale(small_grid_dim/big_grid_dim) + small_grid.center() + pixel = MobjectFromRegion( + region_from_polygon_vertices(*0.2*np.array([ + RIGHT+DOWN, + RIGHT+UP, + LEFT+UP, + LEFT+DOWN + ])) + ) + pixel.set_color(WHITE) + pixel_width = big_grid.width/big_grid.columns + pixel.scale_to_fit_width(pixel_width) + pixel.to_corner(UP+RIGHT, buff = 2) + pixel.shift(5*pixel_width*(2*LEFT+DOWN)) + + freq_line = get_freq_line() + dot = Dot() + dot.shift(freq_line.get_center() + 2*RIGHT) + string = Line(LEFT, RIGHT, color = GREEN) + arrow = Arrow(dot, string.get_center()) + vibration_config = { + "overtones" : 1, + "spatial_period" : 2, + } + vibration, loud_vibration, quiet_vibration = [ + Vibrate(string.copy(), amplitude = a, **vibration_config) + for a in [0.5, 1., 0.25] + ] + + self.add(small_grid) + self.dither() + self.play( + Transform(small_grid, big_grid) + ) + self.play(FadeIn(pixel)) + self.dither() + self.play( + FadeOut(small_grid), + ShowCreation(freq_line) + ) + self.remove(small_grid) + self.play( + Transform(pixel, dot), + ) + self.dither() + self.play(ShowCreation(arrow)) + self.play(loud_vibration) + self.play( + TransformAnimations(loud_vibration, quiet_vibration), + ApplyMethod(dot.fade, 0.9) + ) + self.clear() + self.add(freq_line, dot, arrow) + self.play(quiet_vibration) + + +class ListenToAllPixels(Scene): + def construct(self): + grid = get_grid() + grid.sort_points(np.linalg.norm) + freq_line = get_freq_line() + freq_line.sort_points(lambda p : p[0]) + red, blue = Color(RED), Color(BLUE) + freq_line.gradient_highlight(red, blue) + + colors = [ + Color(rgb = interpolate( + np.array(red.rgb), + np.array(blue.rgb), + alpha + )) + for alpha in np.arange(4)/3. + ] + string = Line(3*LEFT, 3*RIGHT, color = colors[1]) + vibration = Vibrate(string) + vibration_copy = vibration.copy() + vibration_copy.mobject.stroke_width = 1 + sub_vibrations = [ + Vibrate( + string.copy().shift((n-1)*UP).highlight(colors[n]), + overtones = 1, + spatial_period = 6./(n+1), + temporal_period = 1./(n+1), + amplitude = 0.5/(n+1) + ) + for n in range(4) + ] + words = TexMobject("&\\vdots \\\\ \\text{thousands }& \\text{of frequencies} \\\\ &\\vdots") + words.to_edge(UP, buff = 0.1) + + self.add(grid) + self.dither() + self.play(DelayByOrder(ApplyMethod( + grid.gradient_highlight, red, blue + ))) + self.play(Transform(grid, freq_line)) + self.dither() + self.play( + ShimmerIn( + words, + rate_func = squish_rate_func(smooth, 0, 0.2) + ), + *sub_vibrations, + run_time = 5 + ) + self.play( + *[ + TransformAnimations( + sub_vib, vibration + ) + for sub_vib in sub_vibrations + ]+[FadeOut(words)] + ) + self.clear() + self.add(freq_line) + self.play(vibration) + + +class LayAsideSpeculation(Scene): + def construct(self): + words = TextMobject("Would this actually work?") + grid = get_grid() + grid.scale_to_fit_width(6) + grid.to_edge(LEFT) + freq_line = get_freq_line() + freq_line.scale_to_fit_width(6) + freq_line.center().to_edge(RIGHT) + mapping = Mobject( + grid, freq_line, Arrow(grid, freq_line) + ) + mapping.ingest_submobjects() + lower_left = Point().to_corner(DOWN+LEFT, buff = 0) + lower_right = Point().to_corner(DOWN+RIGHT, buff = 0) + + self.add(words) + self.dither() + self.play( + Transform(words, lower_right), + Transform(lower_left, mapping) + ) + self.dither() + + +class RandomMapping(Scene): + def construct(self): + grid = get_grid() + grid.scale_to_fit_width(6) + grid.to_edge(LEFT) + freq_line = get_freq_line() + freq_line.scale_to_fit_width(6) + freq_line.center().to_edge(RIGHT) + # for mob in grid, freq_line: + # indices = np.arange(mob.get_num_points()) + # random.shuffle(indices) + # mob.points = mob.points[indices] + stars = Stars(stroke_width = grid.stroke_width) + + self.add(grid) + targets = [stars, freq_line] + alphas = [not_quite_there(rush_into), rush_from] + for target, rate_func in zip(targets, alphas): + self.play(Transform( + grid, target, + run_time = 3, + rate_func = rate_func, + path_func = path_along_arc(-np.pi/2) + )) + self.dither() + + + +class DataScrambledAnyway(Scene): + def construct(self): + self.add(TextMobject("Data is scrambled anyway, right?")) + self.dither() + + +class LeverageExistingIntuitions(Scene): + def construct(self): + self.add(TextMobject("Leverage existing intuitions")) + self.dither() + + + + +class ThinkInTermsOfReverseMapping(Scene): + def construct(self): + grid = get_grid() + grid.scale_to_fit_width(6) + grid.to_edge(LEFT) + freq_line = get_freq_line() + freq_line.scale_to_fit_width(6) + freq_line.center().to_edge(RIGHT) + arrow = Arrow(grid, freq_line) + + color1, color2 = YELLOW_C, RED + square_length = 0.01 + dot1 = Dot(color = color1) + dot1.shift(3*RIGHT) + dot2 = Dot(color = color2) + dot2.shift(3.1*RIGHT) + arrow1 = Arrow(2*RIGHT+UP, dot1, color = color1, buff = 0.1) + arrow2 = Arrow(4*RIGHT+UP, dot2, color = color2, buff = 0.1) + dot3, arrow3 = [ + mob.copy().shift(5*LEFT+UP) + for mob in dot1, arrow1 + ] + dot4, arrow4 = [ + mob.copy().shift(5*LEFT+0.9*UP) + for mob in dot2, arrow2 + ] + + self.add(grid, freq_line, arrow) + self.dither() + self.play(ApplyMethod( + arrow.rotate, np.pi, + path_func = clockwise_path() + )) + self.dither() + self.play(ShowCreation(arrow1)) + self.add(dot1) + self.play(ShowCreation(arrow2)) + self.add(dot2) + self.dither() + self.remove(arrow1, arrow2) + self.play( + Transform(dot1, dot3), + Transform(dot2, dot4) + ) + self.play( + ApplyMethod(grid.fade, 0.8), + Animation(Mobject(dot3, dot4)) + ) + self.play(ShowCreation(arrow3)) + self.play(ShowCreation(arrow4)) + self.dither() + + +class WeaveLineThroughPixels(Scene): + @staticmethod + def args_to_string(order): + return str(order) + + @staticmethod + def string_to_args(order_str): + return int(order_str) + + def construct(self, order): + start_color, end_color = RED, GREEN + curve = HilbertCurve(order = order) + line = Line(5*LEFT, 5*RIGHT) + for mob in curve, line: + mob.gradient_highlight(start_color, end_color) + freq_line = get_freq_line() + freq_line.replace(line, stretch = True) + + unit = 6./(2**order) #sidelength of pixel + up = unit*UP + right = unit*RIGHT + lower_left = 3*(LEFT+DOWN) + squares = Mobject(*[ + Square( + side_length = unit, + color = WHITE + ).shift(x*right+y*up) + for x, y in it.product(range(2**order), range(2**order)) + ]) + squares.center() + targets = Mobject() + for square in squares.submobjects: + center = square.get_center() + distances = np.apply_along_axis( + lambda p : np.linalg.norm(p-center), + 1, + curve.points + ) + index_along_curve = np.argmin(distances) + fraction_along_curve = index_along_curve/float(curve.get_num_points()) + target = square.copy().center().scale(0.8/(2**order)) + line_index = int(fraction_along_curve*line.get_num_points()) + target.shift(line.points[line_index]) + targets.add(target) + + + self.add(squares) + self.play(ShowCreation( + curve, + run_time = 5, + rate_func = None + )) + self.dither() + self.play( + Transform(curve, line), + Transform(squares, targets), + run_time = 3 + ) + self.dither() + self.play(ShowCreation(freq_line)) + self.dither() + + +class WellPlayedGameOfSnake(Scene): + def construct(self): + grid = Grid(16, 16).fade() + snake_curve = SnakeCurve(order = 4) + words = TextMobject("``Snake Curve''") + words.next_to(grid, UP) + + self.add(grid) + self.play(ShowCreation( + snake_curve, + run_time = 7, + rate_func = None + )) + self.dither() + self.play(ShimmerIn(words)) + self.dither() + + +class TellMathematicianFriend(Scene): + def construct(self): + mathy, bubble = get_mathy_and_bubble() + squiggle_mouth = mathy.mouth.copy() + squiggle_mouth.apply_function( + lambda (x, y, z) : (x, y+0.02*np.sin(50*x), z) + ) + bubble.ingest_submobjects() + bubble.write("Why not use a Hilbert curve \\textinterrobang ") + words1 = bubble.content + bubble.write("So, it's not one curve but an infinite family of curves \\dots") + words2 = bubble.content + bubble.write("Well, no, it \\emph{is} just one thing, but I need \\\\ \ + to tell you about a certain infinite family first.") + words3 = bubble.content + description = TextMobject("Mathematician friend", size = "\\small") + description.next_to(mathy, buff = 2) + arrow = Arrow(description, mathy) + + self.add(mathy) + self.play( + ShowCreation(arrow), + ShimmerIn(description) + ) + self.dither() + point = Point(bubble.get_tip()) + self.play( + Transform(point, bubble), + ) + self.remove(point) + self.add(bubble) + self.play(ShimmerIn(words1)) + self.dither() + self.remove(description, arrow) + self.play( + Transform(mathy.mouth, squiggle_mouth), + ApplyMethod(mathy.arm.wag, 0.2*RIGHT, LEFT), + ) + self.remove(words1) + self.add(words2) + self.dither(2) + self.remove(words2) + self.add(words3) + self.dither(2) + self.play( + ApplyPointwiseFunction( + lambda p : 15*p/np.linalg.norm(p), + bubble + ), + ApplyMethod(mathy.shift, 5*(DOWN+LEFT)), + FadeOut(words3), + run_time = 3 + ) + + +class Order1PseudoHilbertCurve(Scene): + def construct(self): + words, s = TextMobject(["Pseudo-Hilbert Curve", "s"]).split() + pre_words = TextMobject("Order 1") + pre_words.next_to(words, LEFT, buff = 0.5) + s.next_to(words, RIGHT, buff = 0.05, aligned_edge = DOWN) + cluster = Mobject(pre_words, words, s) + cluster.center() + cluster.scale(0.7) + cluster.to_edge(UP, buff = 0.3) + cluster.highlight(GREEN) + grid1 = Grid(1, 1) + grid2 = Grid(2, 2) + curve = HilbertCurve(order = 1) + + self.add(words, s) + self.dither() + self.play(Transform( + s, pre_words, + path_func = path_along_arc(-np.pi/3) + )) + self.dither() + self.play(ShowCreation(grid1)) + self.dither() + self.play(ShowCreation(grid2)) + self.dither() + kwargs = { + "run_time" : 5, + "rate_func" : None + } + self.play(ShowCreation(curve, **kwargs)) + self.dither() + +class Order2PseudoHilbertCurve(Scene): + def construct(self): + words = TextMobject("Order 2 Pseudo-Hilbert Curve") + words.to_edge(UP, buff = 0.3) + words.highlight(GREEN) + grid2 = Grid(2, 2) + grid4 = Grid(4, 4, stroke_width = 2) + # order_1_curve = HilbertCurve(order = 1) + # squaggle_curve = order_1_curve.copy().apply_function( + # lambda (x, y, z) : (x + np.cos(3*y), y + np.sin(3*x), z) + # ) + # squaggle_curve.show() + mini_curves = [ + HilbertCurve(order = 1).scale(0.5).shift(1.5*vect) + for vect in [ + LEFT+DOWN, + LEFT+UP, + RIGHT+UP, + RIGHT+DOWN + ] + ] + last_curve = mini_curves[0] + naive_curve = Mobject(last_curve) + for mini_curve in mini_curves[1:]: + line = Line(last_curve.points[-1], mini_curve.points[0]) + naive_curve.add(line, mini_curve) + last_curve = mini_curve + naive_curve.ingest_submobjects() + naive_curve.gradient_highlight(RED, GREEN) + order_2_curve = HilbertCurve(order = 2) + + self.add(words, grid2) + self.dither() + self.play(ShowCreation(grid4)) + self.play(*[ + ShowCreation(mini_curve) + for mini_curve in mini_curves + ]) + self.dither() + self.play(ShowCreation(naive_curve, run_time = 5)) + self.remove(*mini_curves) + self.dither() + self.play(Transform(naive_curve, order_2_curve)) + self.dither() + + +class Order3PseudoHilbertCurve(Scene): + def construct(self): + words = TextMobject("Order 3 Pseudo-Hilbert Curve") + words.highlight(GREEN) + words.to_edge(UP) + grid4 = Mobject( + Grid(2, 2), + Grid(4, 4, stroke_width = 2) + ) + grid8 = Grid(8, 8, stroke_width = 1) + order_3_curve = HilbertCurve(order = 3) + mini_curves = [ + HilbertCurve(order = 2).scale(0.5).shift(1.5*vect) + for vect in [ + LEFT+DOWN, + LEFT+UP, + RIGHT+UP, + RIGHT+DOWN + ] + ] + + self.add(words, grid4) + self.dither() + self.play(ShowCreation(grid8)) + self.dither() + self.play(*map(GrowFromCenter, mini_curves)) + self.dither() + self.clear() + self.add(words, grid8, *mini_curves) + self.play(*[ + ApplyMethod(curve.rotate_in_place, np.pi, axis) + for curve, axis in [ + (mini_curves[0], UP+RIGHT), + (mini_curves[3], UP+LEFT) + ] + ]) + self.play(ShowCreation(order_3_curve, run_time = 5)) + self.dither() + +class GrowToOrder8PseudoHilbertCurve(Scene): + def construct(self): + self.curve = HilbertCurve(order = 1) + self.add(self.curve) + self.dither() + while self.curve.order < 8: + self.increase_order() + + + def increase_order(self): + mini_curves = [ + self.curve.copy().scale(0.5).shift(1.5*vect) + for vect in [ + LEFT+DOWN, + LEFT+UP, + RIGHT+UP, + RIGHT+DOWN + ] + ] + self.remove(self.curve) + self.play( + Transform(self.curve.copy(), mini_curves[0]) + ) + self.play(*[ + GrowFromCenter(mini_curve) + for mini_curve in mini_curves[1:] + ]) + self.dither() + self.clear() + self.add(*mini_curves) + self.play(*[ + ApplyMethod(curve.rotate_in_place, np.pi, axis) + for curve, axis in [ + (mini_curves[0], UP+RIGHT), + (mini_curves[3], UP+LEFT) + ] + ]) + self.curve = HilbertCurve(order = self.curve.order+1) + self.play(ShowCreation(self.curve, run_time = 2)) + self.remove(*mini_curves) + self.dither() + + +class UseOrder8(Scene): + def construct(self): + mathy, bubble = get_mathy_and_bubble() + bubble.write("For a 256x256 pixel array...") + words = TextMobject("Order 8 Pseudo-Hilbert Curve") + words.highlight(GREEN) + words.to_edge(UP, buff = 0.3) + curve = HilbertCurve(order = 8) + + self.add(mathy, bubble) + self.play(ShimmerIn(bubble.content)) + self.dither() + self.clear() + self.add(words) + self.play(ShowCreation( + curve, run_time = 7, rate_func = None + )) + self.dither() + + + +class HilbertBetterThanSnakeQ(Scene): + def construct(self): + hilbert_curves, snake_curves = [ + [ + CurveClass(order = n) + for n in range(2, 7) + ] + for CurveClass in HilbertCurve, SnakeCurve + ] + for curve in hilbert_curves+snake_curves: + curve.scale(0.8) + for curve in hilbert_curves: + curve.to_edge(LEFT) + for curve in snake_curves: + curve.to_edge(RIGHT) + greater_than = TexMobject(">") + question_mark = TextMobject("?") + question_mark.next_to(greater_than, UP) + + self.add(greater_than, question_mark) + hilbert_curve = hilbert_curves[0] + snake_curve = snake_curves[0] + for new_hc, new_sc in zip(hilbert_curves[1:], snake_curves[1:]): + self.play(*[ + Transform(hilbert_curve, new_hc), + Transform(snake_curve, new_sc) + ]) + self.dither() + + +class ImagineItWorks(Scene): + def construct(self): + self.add(TextMobject("Imagine your project succeeds...")) + self.dither() + + +class RandyWithHeadphones(Scene): + def construct(self): + headphones = ImageMobject("Headphones.png") + headphones.scale(0.1) + headphones.stretch(2, 0) + headphones.shift(1.2*UP+0.05*LEFT) + headphones.highlight(GREY) + randy = Randolph() + + self.add(randy, headphones) + self.dither(2) + self.play(ApplyMethod(randy.blink)) + self.dither(4) + + +class IncreaseResolution(Scene): + def construct(self): + grids = [ + Grid( + 2**order, 2**order, + stroke_width = 1 + ).shift(0.3*DOWN) + for order in 6, 7 + ] + grid = grids[0] + side_brace = Brace(grid, LEFT) + top_brace = Brace(grid, UP) + top_words = TextMobject("256") + new_top_words = TextMobject("512") + side_words = top_words.copy() + new_side_words = new_top_words.copy() + for words in top_words, new_top_words: + words.next_to(top_brace, UP, buff = 0.1) + for words in side_words, new_side_words: + words.next_to(side_brace, LEFT) + + self.add(grid) + self.play( + GrowFromCenter(side_brace), + GrowFromCenter(top_brace), + ShimmerIn(top_words), + ShimmerIn(side_words) + ) + self.dither() + self.play( + DelayByOrder(Transform(*grids)), + Transform(top_words, new_top_words), + Transform(side_words, new_side_words) + ) + self.dither() + + +class IncreasingResolutionWithSnakeCurve(Scene): + def construct(self): + start_curve = SnakeCurve(order = 6) + end_curve = SnakeCurve(order = 7) + start_dots, end_dots = [ + Mobject(*[ + Dot( + curve.points[int(x*curve.get_num_points())], + color = color + ) + for x, color in [ + (0.202, GREEN), + (0.48, BLUE), + (0.7, RED) + ] + ]) + for curve in start_curve, end_curve + ] + self.add(start_curve) + self.dither() + self.play( + ShowCreation(start_dots, run_time = 2), + ApplyMethod(start_curve.fade) + ) + end_curve.fade() + self.play( + Transform(start_curve, end_curve), + Transform(start_dots, end_dots) + ) + self.dither() + + +class TrackSpecificCurvePoint(Scene): + CURVE_CLASS = None #Fillin + def construct(self): + line = get_freq_line().center() + line.sort_points(lambda p : p[0]) + curves = [ + self.CURVE_CLASS(order = order) + for order in range(3, 10) + ] + alpha = 0.48 + dot = Dot(UP) + start_dot = Dot(0.1*LEFT) + dots = [ + Dot(curve.points[alpha*curve.get_num_points()]) + for curve in curves + ] + + self.play(ShowCreation(line)) + self.play(Transform(dot, start_dot)) + self.dither() + for new_dot, curve in zip(dots, curves): + self.play( + Transform(line, curve), + Transform(dot, new_dot) + ) + self.dither() + + +class TrackSpecificSnakeCurvePoint(TrackSpecificCurvePoint): + CURVE_CLASS = SnakeCurve + + +class NeedToRelearn(Scene): + def construct(self): + top_words = TextMobject("Different pixel-frequency association") + bottom_words = TextMobject("Need to relearn sight-via-sound") + top_words.shift(UP) + bottom_words.shift(DOWN) + arrow = Arrow(top_words, bottom_words) + + self.play(ShimmerIn(top_words)) + self.dither() + self.play(ShowCreation(arrow)) + self.play(ShimmerIn(bottom_words)) + self.dither() + + +class TrackSpecificHilbertCurvePoint(TrackSpecificCurvePoint): + CURVE_CLASS = HilbertCurve + + + diff --git a/old_projects/hilbert/section2.py b/old_projects/hilbert/section2.py new file mode 100644 index 00000000..a4549f60 --- /dev/null +++ b/old_projects/hilbert/section2.py @@ -0,0 +1,1095 @@ +from mobject import Mobject, Point, Mobject1D +from mobject.tex_mobject import \ + TexMobject, TextMobject, Brace +from mobject.image_mobject import \ + ImageMobject, MobjectFromRegion + +from scene import Scene + +from animation import Animation +from animation.transform import \ + Transform, CounterclockwiseTransform, ApplyMethod,\ + GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction, \ + ShrinkToCenter, ShimmerIn, FadeOut, FadeIn +from animation.simple_animations import \ + ShowCreation, Homotopy +from animation.meta_animations import \ + DelayByOrder, TransformAnimations +from animation.playground import Vibrate + +from topics.geometry import \ + Line, Dot, Arrow, Grid, Square, Point, Circle +from topics.characters import \ + ThoughtBubble, SpeechBubble, Mathematician +from topics.number_line import UnitInterval, NumberLine +from topics.three_dimensions import Stars +from topics.functions import ParametricFunction + +from mobject.region import region_from_polygon_vertices, Region + +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 + +from helpers import * + + +def get_time_line(): + length = 5.2*SPACE_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 = range(1700, 2100, 100) + ) + time_line.sort_points(lambda p : p[0]) + time_line.gradient_highlight( + PeanoCurve.CONFIG["start_color"], + PeanoCurve.CONFIG["end_color"] + ) + time_line.add_numbers( + 2020, *range(1800, 2050, 50) + ) + return time_line + + +class SectionTwo(Scene): + def construct(self): + self.add(TextMobject("Section 2: Filling space")) + self.dither() + +class HilbertCurveIsPerfect(Scene): + def construct(self): + curve = HilbertCurve(order = 6) + curve.highlight(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.rgbs = sparce_lion.rgbs[closest_point_indices] + line = Line(5*LEFT, 5*RIGHT) + Mobject.align_data(line, colored_curve) + line.rgbs = colored_curve.rgbs + + self.add(lion) + self.play(ShowCreation(curve, run_time = 3)) + self.play( + FadeOut(lion), + Transform(curve, colored_curve), + run_time = 3 + ) + self.dither() + self.play(Transform(curve, line, run_time = 5)) + self.dither() + + +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.dither() + self.play(ApplyMethod( + mathy.blink, + rate_func = squish_rate_func(there_and_back) + )) + self.dither() + self.play(ShowCreation(bubble)) + self.dither() + self.play( + ApplyMethod(mathy.shift, 3*(DOWN+LEFT)), + ApplyPointwiseFunction( + lambda p : 15*p/np.linalg.norm(p), + bubble + ), + run_time = 3 + ) + +class TimeLineAboutSpaceFilling(Scene): + def construct(self): + curve = PeanoCurve(order = 5) + curve.stretch_to_fit_width(2*SPACE_WIDTH) + curve.stretch_to_fit_height(2*SPACE_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.dither() + self.play( + Transform(time_line, curve_start), + FadeOut(brace), + FadeOut(words) + ) + self.play(ShowCreation( + curve, + run_time = 5, + rate_func = None + )) + self.dither() + + + +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.gradient_highlight(curve.start_color, curve.end_color) + for mob in grid, space_mobject: + mob.sort_points(np.linalg.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.dither() + self.play(Transform(grid, space_mobject, run_time = 5)) + self.remove(grid) + self.highlight_region(space_region, DARK_GREY) + self.dither() + self.add(infinitely, detailed) + self.dither() + self.play(DelayByOrder(Transform(detailed, extending))) + self.play(ShowCreation(arrows)) + self.dither() + self.clear() + self.highlight_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.dither() + 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.gradient_highlight(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.dither() + self.play(ShowCreation(squares)) + self.dither() + 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.dither() + self.play(Transform(start_words, end_words)) + self.dither() + self.play(ShowCreation(curve)) + self.dither() + self.play(ShowCreation( + dots, + run_time = 3, + )) + self.dither() + self.clear() + self.play(ShowCreation(fine_curve, run_time = 5)) + self.dither() + self.play(ShimmerIn(space_filling_fractal)) + self.dither() + + +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.dither() + 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.highlight( + Color(rgb = curve.rgbs[curve.get_num_points()/3]) + ) + square.shift( + grid.get_corner(UP+LEFT)-\ + square.get_corner(UP+LEFT) + ) + + + self.dither() + self.play( + FadeOut(grid), + FadeOut(curve), + FadeIn(square) + ) + self.play( + ApplyMethod(square.replace, grid) + ) + self.dither() + + +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.dither() + + +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.dither() + self.play( + FadeOut(words), + ApplyMethod(curves1.replace, curves2), + ShimmerIn(top_words.split()[1]) + ) + self.dither() + self.play(FadeIn(number)) + self.play(ShowCreation(arrow)) + self.play(FadeIn(pair)) + self.dither() + self.play(ShowCreation(number_line)) + self.play(ShowCreation(low_arrow)) + self.play(ShowCreation(grid)) + self.dither() + + +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.dither() + self.remove(function_arrow, function) + self.play(ShowCreation(line)) + self.dither() + self.play( + ShimmerIn(arg), + ShowCreation(arrow1), + ShowCreation(dot1) + ) + self.dither() + self.remove(arrow1) + self.play( + FadeIn(grid), + Transform(line, curve), + Transform(dot1, dot2), + run_time = 2 + ) + self.dither() + self.play( + ShimmerIn(result), + ShowCreation(arrow2) + ) + self.dither() + + + +class ContinuityRequired(Scene): + def construct(self): + words = TextMobject([ + "A function must be", + "\\emph{continuous}", + "if it is to represent a curve." + ]) + words.split()[1].highlight(YELLOW_C) + self.add(words) + self.dither() + + + + +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.highlight(GREEN_A) + + self.interval = UnitInterval() + self.interval.scale_to_fit_width(SPACE_WIDTH-1) + self.interval.to_edge(LEFT) + + self.input_dot = Dot(color = self.input_color) + self.output_dot = self.input_dot.copy().highlight(self.output_color) + left, right = self.interval.get_left(), self.interval.get_right() + self.input_homotopy = lambda (x, y, z, t) : (x, y, t) + interpolate(left, right, t) + output_size = self.output.get_num_points()-1 + output_points = self.output.points + self.output_homotopy = lambda (x, y, z, t) : (x, y, z) + output_points[int(t*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 = np.linalg.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 = np.linalg.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*SPACE_WIDTH/2) + output_space = TextMobject("Output Space") + output_space.to_edge(UP) + output_space.shift(RIGHT*SPACE_WIDTH/2) + line = Line( + UP*SPACE_HEIGHT, DOWN*SPACE_HEIGHT, + color = WHITE + ) + self.play( + ShimmerIn(input_space), + ShimmerIn(output_space), + ShowCreation(line), + ShowCreation(self.interval), + ) + self.dither() + + 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.dither() + + 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.dither() + 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 = 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.dither() + self.remove(A_text, A_arrow, B_text, B_arrow) + self.play(ShowCreation(input_circle)) + self.dither() + self.play(ShowCreation(input_points)) + self.dither() + input_points_copy = input_points.copy() + self.play( + Transform(input_points_copy, output_points), + run_time = 2 + ) + self.dither() + self.play(ShowCreation(output_circle)) + self.dither() + self.dither() + 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.dither() + 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.dither() + 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.dither() + 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.dither() + self.play(ShimmerIn(text)) + self.remove(input_circle, input_points, output_circle, input_points_copy) + self.play(vary_circles) + self.dither() + self.play( + ShimmerIn(discontinuous_at_A), + ShowCreation(discontinuous_arrow) + ) + self.dither(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 update_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.dither() + + +class DefineActualHilbertCurveText(Scene): + def construct(self): + self.add(TextMobject(""" + Finally define a Hilbert Curve\\dots + """)) + self.dither() + + +class ReliesOnWonderfulProperty(Scene): + def construct(self): + self.add(TextMobject(""" + \\dots which relies on a certain property + of Pseudo-Hilbert-curves. + """)) + self.dither() + + +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.dither() + 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.dither() + + text.to_edge(UP) + text_parts = text.split() + for index in 1, -1: + text_parts[index].highlight() + 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.dither() + +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].highlight(BLUE) + parts[3].highlight(BLUE) + parts[1].highlight() + parts[-2].highlight() + 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.dither() + self.play(DelayByOrder(ApplyMethod(starter_dots.shift, 2*DOWN))) + self.dither() + 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.dither() + + +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.highlight(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.dither() + self.remove(explanation, exp_arrow) + self.play(ShowCreation(curve)) + self.play( + ApplyMethod(x1.highlight, BLUE), + ShowCreation(x_arrow), + ShowCreation(dot) + ) + self.dither() + 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.dither() + +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].highlight(BLUE) + definition.split()[-2].highlight(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].highlight(GREEN) + items[3].highlight(YELLOW_C) + items[5].highlight(MAROON) + Mobject(*items).to_edge(RIGHT) + + self.add(definition) + self.play(ShimmerIn(intro)) + self.dither() + self.play(Transform(intro, prove_that)) + for item in items[1:-1]: + self.play(ShimmerIn(item)) + self.dither() + + + +class TilingSpace(Scene): + def construct(self): + coords_set = [ORIGIN] + for n in range(int(2*SPACE_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 = None, + 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.highlight, + RED, + lambda p : p[0] > n-6.2 and p[0] < n-4 and p[1] > -0.4 + ), + brace_anim + ) + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/old_projects/hilbert/section3.py b/old_projects/hilbert/section3.py new file mode 100644 index 00000000..b14de0bf --- /dev/null +++ b/old_projects/hilbert/section3.py @@ -0,0 +1,310 @@ +from mobject import Mobject, Point +from mobject.tex_mobject import \ + TexMobject, TextMobject, Brace +from mobject.image_mobject import \ + ImageMobject, MobjectFromRegion + +from scene import Scene + +from animation import Animation +from animation.transform import \ + Transform, CounterclockwiseTransform, ApplyMethod,\ + GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction,\ + TransformAnimations, ShimmerIn, FadeOut, FadeIn +from animation.simple_animations import \ + ShowCreation, DelayByOrder +from animation.playground import Vibrate + +from topics.geometry import \ + Line, Dot, Arrow, Grid, Square, Point, Polygon +from topics.characters import \ + ThoughtBubble, SpeechBubble, Mathematician, Mortimer +from topics.number_line import UnitInterval +from topics.three_dimensions import Stars + +from mobject.region import region_from_polygon_vertices + +import displayer as disp + +from hilbert.curves import \ + TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \ + SnakeCurve, Sierpinski +from hilbert.section1 import get_mathy_and_bubble + + +from helpers import * + + + +class SectionThree(Scene): + def construct(self): + self.add(TextMobject("A few words on the usefulness of infinite math")) + self.dither() + + +class InfiniteResultsFiniteWorld(Scene): + def construct(self): + left_words = TextMobject("Infinite result") + right_words = TextMobject("Finite world") + for words in left_words, right_words: + words.scale(0.8) + left_formula = TexMobject( + "\\sum_{n = 0}^{\\infty} 2^n = -1" + ) + right_formula = TexMobject("111\\cdots111") + for formula in left_formula, right_formula: + formula.add( + Brace(formula, UP), + ) + formula.ingest_submobjects() + right_overwords = TextMobject( + "\\substack{\ + \\text{How computers} \\\\ \ + \\text{represent $-1$}\ + }" + ).scale(1.5) + + left_mobs = [left_words, left_formula] + right_mobs = [right_words, right_formula] + for mob in left_mobs: + mob.to_edge(RIGHT, buff = 1) + mob.shift(SPACE_WIDTH*LEFT) + for mob in right_mobs: + mob.to_edge(LEFT, buff = 1) + mob.shift(SPACE_WIDTH*RIGHT) + arrow = Arrow(left_words, right_words) + right_overwords.next_to(right_formula, UP) + + self.play(ShimmerIn(left_words)) + self.play(ShowCreation(arrow)) + self.play(ShimmerIn(right_words)) + self.dither() + self.play( + ShimmerIn(left_formula), + ApplyMethod(left_words.next_to, left_formula, UP) + ) + self.dither() + self.play( + ShimmerIn(right_formula), + Transform(right_words, right_overwords) + ) + self.dither() + self.finite_analog( + Mobject(left_formula, left_words), + arrow, + Mobject(right_formula, right_words) + ) + + + def finite_analog(self, left_mob, arrow, right_mob): + self.clear() + self.add(left_mob, arrow, right_mob) + ex = TextMobject("\\times") + ex.highlight(RED) + # ex.shift(arrow.get_center()) + middle = TexMobject( + "\\sum_{n=0}^N 2^n \\equiv -1 \\mod 2^{N+1}" + ) + finite_analog = TextMobject("Finite analog") + finite_analog.scale(0.8) + brace = Brace(middle, UP) + finite_analog.next_to(brace, UP) + new_left = left_mob.copy().to_edge(LEFT) + new_right = right_mob.copy().to_edge(RIGHT) + left_arrow, right_arrow = [ + Arrow( + mob1.get_right()[0]*RIGHT, + mob2.get_left()[0]*RIGHT, + buff = 0 + ) + for mob1, mob2 in [ + (new_left, middle), + (middle, new_right) + ] + ] + for mob in ex, middle: + mob.sort_points(np.linalg.norm) + + self.play(GrowFromCenter(ex)) + self.dither() + self.play( + Transform(left_mob, new_left), + Transform(arrow.copy(), left_arrow), + DelayByOrder(Transform(ex, middle)), + Transform(arrow, right_arrow), + Transform(right_mob, new_right) + ) + self.play( + GrowFromCenter(brace), + ShimmerIn(finite_analog) + ) + self.dither() + self.equivalence( + left_mob, + left_arrow, + Mobject(middle, brace, finite_analog) + ) + + def equivalence(self, left_mob, arrow, right_mob): + self.clear() + self.add(left_mob, arrow, right_mob) + words = TextMobject("is equivalent to") + words.shift(0.25*LEFT) + words.highlight(BLUE) + new_left = left_mob.copy().shift(RIGHT) + new_right = right_mob.copy() + new_right.shift( + (words.get_right()[0]-\ + right_mob.get_left()[0]+\ + 0.5 + )*RIGHT + ) + for mob in arrow, words: + mob.sort_points(np.linalg.norm) + + self.play( + ApplyMethod(left_mob.shift, RIGHT), + Transform(arrow, words), + ApplyMethod(right_mob.to_edge, RIGHT) + ) + self.dither() + + +class HilbertCurvesStayStable(Scene): + def construct(self): + scale_factor = 0.9 + grid = Grid(4, 4, stroke_width = 1) + curve = HilbertCurve(order = 2) + for mob in grid, curve: + mob.scale(scale_factor) + words = TextMobject(""" + Sequence of curves is stable + $\\leftrightarrow$ existence of limit curve + """, size = "\\normal") + words.scale(1.25) + words.to_edge(UP) + + self.add(curve, grid) + self.dither() + for n in range(3, 7): + if n == 5: + self.play(ShimmerIn(words)) + new_grid = Grid(2**n, 2**n, stroke_width = 1) + new_curve = HilbertCurve(order = n) + for mob in new_grid, new_curve: + mob.scale(scale_factor) + self.play( + ShowCreation(new_grid), + Animation(curve) + ) + self.remove(grid) + grid = new_grid + self.play(Transform(curve, new_curve)) + self.dither() + + + +class InfiniteObjectsEncapsulateFiniteObjects(Scene): + def get_triangles(self): + triangle = Polygon( + LEFT/np.sqrt(3), + UP, + RIGHT/np.sqrt(3), + color = GREEN + ) + triangles = Mobject( + triangle.copy().scale(0.5).shift(LEFT), + triangle, + triangle.copy().scale(0.3).shift(0.5*UP+RIGHT) + ) + triangles.center() + return triangles + + def construct(self): + words =[ + TextMobject(text, size = "\\large") + for text in [ + "Truths about infinite objects", + " encapsulate ", + "facts about finite objects" + ] + ] + + words[0].highlight(RED) + words[1].next_to(words[0]) + words[2].highlight(GREEN).next_to(words[1]) + Mobject(*words).center().to_edge(UP) + infinite_objects = [ + TexMobject( + "\\sum_{n=0}^\\infty", + size = "\\normal" + ).highlight(RED_E), + Sierpinski(order = 8).scale(0.3), + TextMobject( + "$\\exists$ something infinite $\\dots$" + ).highlight(RED_B) + ] + finite_objects = [ + TexMobject( + "\\sum_{n=0}^N", + size = "\\normal" + ).highlight(GREEN_E), + self.get_triangles(), + TextMobject( + "$\\forall$ finite somethings $\\dots$" + ).highlight(GREEN_B) + ] + for infinite, finite, n in zip(infinite_objects, finite_objects, it.count(1, 2)): + infinite.next_to(words[0], DOWN, buff = n) + finite.next_to(words[2], DOWN, buff = n) + + self.play(ShimmerIn(words[0])) + self.dither() + self.play(ShimmerIn(infinite_objects[0])) + self.play(ShowCreation(infinite_objects[1])) + self.play(ShimmerIn(infinite_objects[2])) + self.dither() + self.play(ShimmerIn(words[1]), ShimmerIn(words[2])) + self.play(ShimmerIn(finite_objects[0])) + self.play(ShowCreation(finite_objects[1])) + self.play(ShimmerIn(finite_objects[2])) + self.dither() + + +class StatementRemovedFromReality(Scene): + def construct(self): + mathy, bubble = get_mathy_and_bubble() + bubble.stretch_to_fit_width(4) + mathy.to_corner(DOWN+LEFT) + bubble.pin_to(mathy) + bubble.shift(LEFT) + morty = Mortimer() + morty.to_corner(DOWN+RIGHT) + morty_bubble = SpeechBubble() + morty_bubble.stretch_to_fit_width(4) + morty_bubble.pin_to(morty) + bubble.write(""" + Did you know a curve \\\\ + can fill all space? + """) + morty_bubble.write("Who cares?") + + self.add(mathy, morty) + for bub, buddy in [(bubble, mathy), (morty_bubble, morty)]: + self.play(Transform( + Point(bub.get_tip()), + bub + )) + self.play(ShimmerIn(bub.content)) + self.play(ApplyMethod( + buddy.blink, + rate_func = squish_rate_func(there_and_back) + )) + + + + + + + + diff --git a/old_projects/number_line_scene.py b/old_projects/number_line_scene.py new file mode 100644 index 00000000..88303a72 --- /dev/null +++ b/old_projects/number_line_scene.py @@ -0,0 +1,83 @@ +class NumberLineScene(Scene): + def construct(self, **number_line_config): + self.number_line = NumberLine(**number_line_config) + self.displayed_numbers = self.number_line.default_numbers_to_display() + self.number_mobs = self.number_line.get_number_mobjects(*self.displayed_numbers) + self.add(self.number_line, *self.number_mobs) + + def zoom_in_on(self, number, zoom_factor, run_time = 2.0): + unit_length_to_spatial_width = self.number_line.unit_length_to_spatial_width*zoom_factor + radius = SPACE_WIDTH/unit_length_to_spatial_width + tick_frequency = 10**(np.floor(np.log10(radius))) + left_tick = tick_frequency*(np.ceil((number-radius)/tick_frequency)) + new_number_line = NumberLine( + numerical_radius = radius, + unit_length_to_spatial_width = unit_length_to_spatial_width, + tick_frequency = tick_frequency, + leftmost_tick = left_tick, + number_at_center = number + ) + new_displayed_numbers = new_number_line.default_numbers_to_display() + new_number_mobs = new_number_line.get_number_mobjects(*new_displayed_numbers) + + transforms = [] + additional_mobjects = [] + squished_new_line = new_number_line.copy() + squished_new_line.scale(1.0/zoom_factor) + squished_new_line.shift(self.number_line.number_to_point(number)) + squished_new_line.points[:,1] = self.number_line.number_to_point(0)[1] + transforms.append(Transform(squished_new_line, new_number_line)) + for mob, num in zip(new_number_mobs, new_displayed_numbers): + point = Point(self.number_line.number_to_point(num)) + point.shift(new_number_line.get_vertical_number_offset()) + transforms.append(Transform(point, mob)) + for mob in self.mobjects: + if mob == self.number_line: + new_mob = mob.copy() + new_mob.shift(-self.number_line.number_to_point(number)) + new_mob.stretch(zoom_factor, 0) + transforms.append(Transform(mob, new_mob)) + continue + mob_center = mob.get_center() + number_under_center = self.number_line.point_to_number(mob_center) + new_point = new_number_line.number_to_point(number_under_center) + new_point += mob_center[1]*UP + if mob in self.number_mobs: + transforms.append(Transform(mob, Point(new_point))) + else: + transforms.append(ApplyMethod(mob.shift, new_point - mob_center)) + additional_mobjects.append(mob) + line_to_hide_pixelation = Line( + self.number_line.get_left(), + self.number_line.get_right(), + color = self.number_line.get_color() + ) + self.add(line_to_hide_pixelation) + self.play(*transforms, run_time = run_time) + self.clear() + self.number_line = new_number_line + self.displayed_numbers = new_displayed_numbers + self.number_mobs = new_number_mobs + self.add(self.number_line, *self.number_mobs) + self.add(*additional_mobjects) + + def show_multiplication(self, num, **kwargs): + if "path_func" not in kwargs: + if num > 0: + kwargs["path_func"] = straight_path + else: + kwargs["path_func"] = counterclockwise_path() + self.play(*[ + ApplyMethod(self.number_line.stretch, num, 0, **kwargs) + ]+[ + ApplyMethod(mob.shift, (num-1)*mob.get_center()[0]*RIGHT, **kwargs) + for mob in self.number_mobs + ]) + + + + + + + + diff --git a/old_projects/triangle_of_power/__init__.py b/old_projects/triangle_of_power/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/triangle_of_power/end.py b/old_projects/triangle_of_power/end.py similarity index 100% rename from triangle_of_power/end.py rename to old_projects/triangle_of_power/end.py diff --git a/triangle_of_power/intro.py b/old_projects/triangle_of_power/intro.py similarity index 100% rename from triangle_of_power/intro.py rename to old_projects/triangle_of_power/intro.py diff --git a/triangle_of_power/triangle.py b/old_projects/triangle_of_power/triangle.py similarity index 100% rename from triangle_of_power/triangle.py rename to old_projects/triangle_of_power/triangle.py