diff --git a/fractal_charm.py b/fractal_charm.py new file mode 100644 index 00000000..377cb748 --- /dev/null +++ b/fractal_charm.py @@ -0,0 +1,167 @@ +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.fractals import * +from topics.number_line import * +from topics.combinatorics import * +from topics.numerals import * +from topics.three_dimensions import * +from scene import Scene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + +class FractalCreation(Scene): + CONFIG = { + "fractal_class" : PentagonalFractal, + "max_order" : 5, + "transform_kwargs" : { + "path_arc" : np.pi/6, + "submobject_mode" : "lagged_start", + "run_time" : 2, + }, + "fractal_kwargs" : {}, + "modify_stroke_width" : True, + } + def construct(self): + fractal = self.fractal_class(order = 0, **self.fractal_kwargs) + self.play(FadeIn(fractal)) + for order in range(1, self.max_order+1): + new_fractal = self.fractal_class( + order = order, + **self.fractal_kwargs + ) + if self.modify_stroke_width: + smallest = new_fractal.family_members_with_points()[0] + if smallest.get_width() < 0.25: + new_fractal.set_stroke(width = 2) + if smallest.get_width() < 0.1: + new_fractal.set_stroke(width = 1) + self.play(Transform( + fractal, new_fractal, + **self.transform_kwargs + )) + self.dither() + self.dither() + self.fractal = fractal + +class PentagonalFractalCreation(FractalCreation): + pass + +class DiamondFractalCreation(FractalCreation): + CONFIG = { + "fractal_class" : DiamondFractal, + "max_order" : 6, + "fractal_kwargs" : {"height" : 6} + } + + +class PiCreatureFractalCreation(FractalCreation): + CONFIG = { + "fractal_class" : PiCreatureFractal, + "max_order" : 5, + "fractal_kwargs" : {"height" : 6}, + "transform_kwargs" : { + "submobject_mode" : "all_at_once", + "run_time" : 2, + }, + } + def construct(self): + FractalCreation.construct(self) + fractal = self.fractal + smallest_pi = fractal[0][0] + zoom_factor = 0.1/smallest_pi.get_height() + fractal.generate_target() + fractal.target.shift(-smallest_pi.get_corner(UP+LEFT)) + fractal.target.scale(zoom_factor) + self.play(MoveToTarget(fractal, run_time = 10)) + self.dither() + +class QuadraticKochFractalCreation(FractalCreation): + CONFIG = { + "fractal_class" : QuadraticKoch, + "max_order" : 5, + "fractal_kwargs" : {"radius" : 10}, + # "transform_kwargs" : { + # "submobject_mode" : "all_at_once", + # "run_time" : 2, + # }, + } + +class KochSnowFlakeFractalCreation(FractalCreation): + CONFIG = { + "fractal_class" : KochSnowFlake, + "max_order" : 5, + "fractal_kwargs" : {"radius" : 6}, + "transform_kwargs" : { + "submobject_mode" : "lagged_start", + "path_arc" : np.pi/6, + "run_time" : 2, + }, + } + + +class WonkyHexagonFractalCreation(FractalCreation): + CONFIG = { + "fractal_class" : WonkyHexagonFractal, + "max_order" : 5, + "fractal_kwargs" : {"height" : 6}, + "transform_kwargs" : { + "submobject_mode" : "lagged_start", + "path_arc" : np.pi/6, + "run_time" : 2, + }, + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fractal_dimension.py b/fractal_dimension.py index f4b5f1bb..0ad1028f 100644 --- a/fractal_dimension.py +++ b/fractal_dimension.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from helpers import * from mobject.tex_mobject import TexMobject @@ -24,6 +22,8 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * +from fractal_charm import FractalCreation + from eoc.graph_scene import GraphScene from eoc.chapter1 import PatreonThanks @@ -82,26 +82,7 @@ class SierpinskiTest(Scene): # self.play(sierp.scale, 2, sierp.get_top()) # self.dither(3) -class FractalCreation(Scene): - CONFIG = { - "fractal_class" : PentagonalFractal, - "max_order" : 5, - "path_arc" : np.pi/6, - "submobject_mode" : "lagged_start" - } - def construct(self): - fractal = self.fractal_class(order = 0) - self.play(FadeIn(fractal)) - for order in range(1, self.max_order+1): - new_fractal = self.fractal_class(order = order) - self.play(Transform( - fractal, new_fractal, - path_arc = self.path_arc, - submobject_mode = self.submobject_mode, - run_time = 2 - )) - self.dither() - self.dither() + ################################### @@ -2312,7 +2293,7 @@ class IntroduceLogLogPlot(GraphScene): class ManyBritainCounts(BoxCountingWithBritain): CONFIG = { "box_width" : 0.1, - "num_boundary_check_points" : 5000, + "num_boundary_check_points" : 10000, "corner_rect_left_extension" : 1, } def construct(self): @@ -2325,8 +2306,8 @@ class ManyBritainCounts(BoxCountingWithBritain): self.add(self.get_grid()) self.add(britain) - for width in range(1, 6): - self.play(britain.scale_to_fit_width, width) + for x in range(5): + self.play(britain.scale, 2, britain.point_from_proportion(0.8)) boxes = self.get_highlighted_boxes(britain) self.play(ShowCreation(boxes)) self.dither() @@ -2360,6 +2341,49 @@ class DefineFractal(TeacherStudentsScene): ]) self.dither(3) +class RoughnessAndFractionalDimension(Scene): + def construct(self): + title = TextMobject( + "Non-integer dimension $\\Leftrightarrow$ Roughness" + ) + title.to_edge(UP) + self.add(title) + + randy = Randolph().scale(2) + randy.to_corner(DOWN+RIGHT) + self.add(randy) + + target = randy.copy() + target.change_mode("hooray") + ponder_target = randy.copy() + ponder_target.change_mode("pondering") + for mob in target, ponder_target: + fractalify(mob, order = 2) + + dimension_label = TextMobject("Boundary dimension = ", "1") + dimension_label.to_edge(LEFT) + one = dimension_label[1] + one.highlight(BLUE) + new_dim = TexMobject("1.2") + new_dim.move_to(one, DOWN+LEFT) + new_dim.highlight(one.get_color()) + self.add(dimension_label) + + self.play(Blink(randy)) + self.play( + Transform(randy, target, run_time = 2), + Transform(one, new_dim) + ) + self.dither() + self.play(Blink(randy)) + self.play(randy.look, DOWN+RIGHT) + self.dither() + self.play(randy.look, DOWN+LEFT) + self.play(Blink(randy)) + self.dither() + self.play(Transform(randy, ponder_target)) + self.dither() + class DifferentSlopesAtDifferentScales(IntroduceLogLogPlot): def construct(self): self.setup_axes(animate = False) @@ -2401,14 +2425,53 @@ class DifferentSlopesAtDifferentScales(IntroduceLogLogPlot): class HoldUpCoilExample(TeacherStudentsScene): def construct(self): + point = UP+RIGHT self.play( self.get_teacher().change_mode, "raise_right_hand", - self.get_teacher().look, UP+LEFT + self.get_teacher().look_at, point ) + self.play(*[ + ApplyMethod(pi.look_at, point) + for pi in self.get_students() + ]) self.dither(5) self.change_student_modes(*["thinking"]*3) + self.play(*[ + ApplyMethod(pi.look_at, point) + for pi in self.get_students() + ]) self.dither(5) +class SmoothHilbertZoom(Scene): + def construct(self): + hilbert = HilbertCurve( + order = 7, + color = MAROON_B, + monochromatic = True + ) + hilbert.make_smooth() + self.add(hilbert) + + two_d_title = TextMobject("2D at a distance...") + one_d_title = TextMobject("1D up close") + for title in two_d_title, one_d_title: + title.to_edge(UP) + + self.add(two_d_title) + self.dither() + self.play( + ApplyMethod( + hilbert.scale, 100, + hilbert.point_from_proportion(0.3), + ), + Transform( + two_d_title, one_d_title, + rate_func = squish_rate_func(smooth) + ), + run_time = 3 + ) + self.dither() + class ListDimensionTypes(PiCreatureScene): CONFIG = { "use_morty" : False, @@ -2777,7 +2840,7 @@ class FractalNonFractalFlowChart(Scene): class ShowPiCreatureFractalCreation(FractalCreation): CONFIG = { - "fractal_class" : PiCreatureFractal, + "fractal_class" : PentagonalPiCreatureFractal, "max_order" : 4, } diff --git a/topics/characters.py b/topics/characters.py index 4f075504..b233196a 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -177,6 +177,17 @@ class PiCreature(SVGMobject): return self +def get_all_pi_creature_modes(): + result = [] + prefix = "PiCreatures_" + suffix = ".svg" + for file in os.listdir(PI_CREATURE_DIR): + if file.startswith(prefix) and file.endswith(suffix): + result.append( + file[len(prefix):-len(suffix)] + ) + return result + class Randolph(PiCreature): pass #Nothing more than an alternative name diff --git a/topics/fractals.py b/topics/fractals.py index c04da34b..80c6450d 100644 --- a/topics/fractals.py +++ b/topics/fractals.py @@ -3,8 +3,8 @@ from mobject.vectorized_mobject import VMobject, VGroup, VectorizedPoint from scene import Scene from animation.transform import Transform from animation.simple_animations import ShowCreation -from topics.geometry import Line, Polygon, RegularPolygon -from characters import PiCreature, Randolph +from topics.geometry import Line, Polygon, RegularPolygon, Square +from characters import PiCreature, Randolph, get_all_pi_creature_modes from helpers import * @@ -20,10 +20,7 @@ def fractalify(vmobject, order = 3, *args, **kwargs): fractalification_iteration(vmobject) return vmobject -def fractalification_iteration(vmobject, - dimension = 1.05, - num_inserted_anchors_range = range(1, 4) - ): +def fractalification_iteration(vmobject, dimension = 1.05, num_inserted_anchors_range = range(1, 4)): num_points = vmobject.get_num_points() if num_points > 0: # original_anchors = vmobject.get_anchors() @@ -147,7 +144,7 @@ class PentagonalFractal(SelfSimilarFractal): part.shift(0.95*part.get_height()*UP) part.rotate(2*np.pi*x/5) -class PiCreatureFractal(PentagonalFractal): +class PentagonalPiCreatureFractal(PentagonalFractal): def init_colors(self): SelfSimilarFractal.init_colors(self) internal_pis = [ @@ -170,6 +167,88 @@ class PiCreatureFractal(PentagonalFractal): PentagonalFractal.arrange_subparts(self, *subparts) +class PiCreatureFractal(VMobject): + CONFIG = { + "order" : 7, + "scale_val" : 1.7, + "start_mode" : "hooray", + "height" : 6, + "colors" : [ + BLUE_D, BLUE_B, MAROON_B, MAROON_D, GREY, + YELLOW, RED, GREY_BROWN, RED, RED_E, + ], + "random_seed" : 0, + "stroke_width" : 0, + } + def init_colors(self): + VMobject.init_colors(self) + internal_pis = [ + pi + for pi in self.submobject_family() + if isinstance(pi, PiCreature) + ] + random.seed(self.random_seed) + for pi in reversed(internal_pis): + color = random.choice(self.colors) + pi.highlight(color) + pi.set_stroke(color, width = 0) + + + def generate_points(self): + random.seed(self.random_seed) + modes = get_all_pi_creature_modes() + seed = PiCreature(mode = self.start_mode) + seed.scale_to_fit_height(self.height) + seed.to_edge(DOWN) + creatures = [seed] + self.add(seed) + for x in range(self.order): + new_creatures = [] + for creature in creatures: + for eye in creature.eyes: + new_creature = PiCreature( + mode = random.choice(modes) + ) + new_creature.replace(eye) + new_creature.scale( + self.scale_val, + about_point = new_creature.get_bottom() + ) + new_creatures.append(new_creature) + creature.blink() + self.add_to_back(VGroup(*new_creatures)) + creatures = new_creatures + + # def init_colors(self): + # VMobject.init_colors(self) + # self.gradient_highlight(*self.colors) + + +class WonkyHexagonFractal(SelfSimilarFractal): + CONFIG = { + "num_subparts" : 7 + } + def get_seed_shape(self): + return RegularPolygon(n=6) + + def arrange_subparts(self, *subparts): + for i, piece in enumerate(subparts): + piece.rotate(i*np.pi/12) + p1, p2, p3, p4, p5, p6, p7 = subparts + center_row = VGroup(p1, p4, p7) + center_row.arrange_submobjects(RIGHT, buff = 0) + for p in p2, p3, p5, p6: + p.scale_to_fit_width(p1.get_width()) + p2.move_to(p1.get_top(), DOWN+LEFT) + p3.move_to(p1.get_bottom(), UP+LEFT) + p5.move_to(p4.get_top(), DOWN+LEFT) + p6.move_to(p4.get_bottom(), UP+LEFT) + + + + + + ######## Space filling curves ############ class FractalCurve(VMobject):