diff --git a/hilbert/curves.py b/hilbert/curves.py index 65561fd2..8d7011f9 100644 --- a/hilbert/curves.py +++ b/hilbert/curves.py @@ -262,6 +262,18 @@ class Sierpinski(LindenmayerCurve): } +class StellarCurve(LindenmayerCurve): + DEFAULT_CONFIG = { + "start_color" : RED, + "end_color" : BLUE_E, + "rule" : { + "A" : "+B-A-B+A-B+", + "B" : "-A+B+A-B+A-", + }, + "scale_factor" : 3, + "angle" : 2*np.pi/5, + } + class SnakeCurve(SpaceFillingCurve): DEFAULT_CONFIG = { "start_color" : BLUE, diff --git a/hilbert/section1.py b/hilbert/section1.py index 6707363b..aacdc494 100644 --- a/hilbert/section1.py +++ b/hilbert/section1.py @@ -528,8 +528,8 @@ class ThinkInTermsOfReverseMapping(Scene): dot1.shift(3*RIGHT) dot2 = Dot(color = color2) dot2.shift(3.1*RIGHT) - arrow1 = Arrow(2*RIGHT+UP, dot1, color = color1, buffer = 0.1) - arrow2 = Arrow(4*RIGHT+UP, dot2, color = color2, buffer = 0.1) + 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 diff --git a/hilbert/section2.py b/hilbert/section2.py index 6ccd446e..62bdbd86 100644 --- a/hilbert/section2.py +++ b/hilbert/section2.py @@ -1,4 +1,4 @@ -from mobject import Mobject, Point +from mobject import Mobject, Point, Mobject1D from mobject.tex_mobject import \ TexMobject, TextMobject, Brace from mobject.image_mobject import \ @@ -9,7 +9,8 @@ from scene import Scene from animation import Animation from animation.transform import \ Transform, CounterclockwiseTransform, ApplyMethod,\ - GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction + GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction, \ + ShrinkToCenter from animation.simple_animations import \ ShowCreation, ShimmerIn, FadeOut, FadeIn from animation.meta_animations import \ @@ -20,24 +21,45 @@ from topics.geometry import \ Line, Dot, Arrow, Grid, Square, Point from topics.characters import \ ThoughtBubble, SpeechBubble, Mathematician -from topics.number_line import UnitInterval +from topics.number_line import UnitInterval, NumberLine from topics.three_dimensions import Stars -from region import region_from_polygon_vertices +from region import region_from_polygon_vertices, Region import displayer as disp from hilbert.curves import \ TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \ - SnakeCurve - -from section1 import get_mathy_and_bubble + 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.DEFAULT_CONFIG["start_color"], + PeanoCurve.DEFAULT_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")) @@ -46,6 +68,7 @@ class SectionTwo(Scene): 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) @@ -76,18 +99,410 @@ class HilbertCurveIsPerfect(Scene): 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, + alpha_func = squish_alpha_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, + alpha_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, point_thickness=1) + self.add(grid, curve) + for order in range(2, 6): + self.dither() + new_grid = Grid(2**order, 2**order, point_thickness=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_sub_mobjects() + 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.play(ShimmerIn(TextMobject(" ".join([ + "$f : A \\to B$ is continuous if: \\\\ \n\n", + "$\\forall x \\in A$,", + "$\\forall \\epsilon > 0$,", + "$\\exists \\delta > 0$ such that", + "$|f(y) - f(x)| < \\epsilon$", + "for all $y \\in A$ satisfying $|x-y|<\\delta$.", + ])))) + self.dither() diff --git a/mobject/mobject.py b/mobject/mobject.py index ea238daf..0170f874 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -154,7 +154,7 @@ class Mobject(object): ) return self - def highlight(self, color = "yellow", condition = None): + def highlight(self, color = YELLOW_C, condition = None): """ Condition is function which takes in one arguments, (x, y, z). """ @@ -180,6 +180,11 @@ class Mobject(object): ]) return self + def match_colors(self, mobject): + Mobject.align_data(self, mobject) + self.rgbs = np.array(mobject.rgbs) + return self + def filter_out(self, condition): for mob in self.nonempty_family_members(): to_eliminate = ~np.apply_along_axis(condition, 1, mob.points)