diff --git a/fractal_dimension.py b/fractal_dimension.py index f2b85e52..e794d1cd 100644 --- a/fractal_dimension.py +++ b/fractal_dimension.py @@ -25,6 +25,118 @@ from mobject.svg_mobject import * from mobject.tex_mobject import * -class Test(Scene): +class KochTest(Scene): def construct(self): - pass \ No newline at end of file + koch = KochCurve(order = 5, stroke_width = 2) + + self.play(ShowCreation(koch, run_time = 3)) + self.play( + koch.scale, 3, koch.get_left(), + koch.set_stroke, None, 4 + ) + self.dither() + +class SierpinskiTest(Scene): + def construct(self): + sierp = Sierpinski( + order = 5, + ) + + self.play(FadeIn( + sierp, + run_time = 5, + submobject_mode = "lagged_start", + )) + self.dither() + # self.play(sierp.scale, 2, sierp.get_top()) + # self.dither(3) + +################################### + + +class ZoomInOnFractal(PiCreatureScene): + CONFIG = { + "fractal_order" : 6, + "num_zooms" : 4, + "fractal_class" : Sierpinski, + "index_to_replace" : 0, + } + def construct(self): + morty = self.pi_creature + fractal = self.get_zoomable_fractal() + + fractal.show() + + self.play( + ShowCreation( + fractal, + run_time = 4, + rate_func = rush_from + ), + morty.change_mode, "hooray", + ) + self.dither() + self.play( + ApplyMethod( + fractal.scale, 2**self.num_zooms, + self.zoom_in_about_point, + run_time = 8 + ), + morty.change_mode, "thinking", + morty.look_at, fractal.get_corner(self.zoom_in_about_point), + ) + self.play(Blink(morty)) + self.dither() + + def get_zoomable_fractal(self): + fractal = self.fractal_class(order = self.fractal_order) + + to_be_tweaked = fractal + for x in range(self.num_zooms): + new_corner = self.fractal_class(order = self.fractal_order) + new_corner.replace(to_be_tweaked[self.index_to_replace]) + self.tweak_subpart(new_corner) + to_be_tweaked.submobjects[self.index_to_replace] = new_corner + to_be_tweaked = new_corner + self.zoom_in_about_point = to_be_tweaked.get_center() + + return fractal + + def tweak_subpart(self, subpart): + pass + + +class ZoomInOnDiamondFractal(ZoomInOnFractal): + CONFIG = { + "fractal_order" : 5, + "fractal_class" : DiamondFractal, + } + def construct(self): + high_res_fractal = self.fractal_class(order = self.fractal_order) + low_res_fractal = self.fractal_class(order = self.fractal_order-1) + + high_res_fractal.scale(3, high_res_fractal.get_top()) + + self.add(low_res_fractal) + self.dither() + self.play(Transform(low_res_fractal, high_res_fractal)) + self.dither(3) + + + + + + + + + + + + + + + + + + + diff --git a/mobject/vectorized_mobject.py b/mobject/vectorized_mobject.py index bcc077d4..2aedb1c5 100644 --- a/mobject/vectorized_mobject.py +++ b/mobject/vectorized_mobject.py @@ -17,9 +17,6 @@ class VMobject(Mobject): "considered_smooth" : True, "propogate_style_to_family" : False, } - def __init__(self, *args, **kwargs): - Mobject.__init__(self, *args, **kwargs) - VMobject.init_colors(self) ## Colors def init_colors(self): diff --git a/topics/characters.py b/topics/characters.py index 7a0a7719..04c6be7e 100644 --- a/topics/characters.py +++ b/topics/characters.py @@ -29,7 +29,9 @@ class PiCreature(SVGMobject): CONFIG = { "color" : BLUE_E, "stroke_width" : 0, + "stroke_color" : BLACK, "fill_opacity" : 1.0, + "propogate_style_to_family" : True, "initial_scale_factor" : 0.01, "corner_scale_factor" : 0.75, "flip_at_start" : False, @@ -44,7 +46,6 @@ class PiCreature(SVGMobject): ) digest_config(self, kwargs, locals()) SVGMobject.__init__(self, file_name = svg_file, **kwargs) - self.init_colors() if self.flip_at_start: self.flip() if self.start_corner is not None: @@ -66,7 +67,7 @@ class PiCreature(SVGMobject): self.parts_named = True def init_colors(self): - self.set_stroke(color = BLACK, width = self.stroke_width) + SVGMobject.init_colors(self) if not self.parts_named: self.name_parts() self.mouth.set_fill(BLACK, opacity = 1) diff --git a/topics/fractals.py b/topics/fractals.py index 6fd7bbb4..d8756fee 100644 --- a/topics/fractals.py +++ b/topics/fractals.py @@ -3,7 +3,7 @@ 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 +from topics.geometry import Line, Polygon, RegularPolygon from helpers import * @@ -15,19 +15,92 @@ def rotate(points, angle = np.pi, axis = OUT): return points +class SelfSimilarFractal(VMobject): + CONFIG = { + "order" : 5, + "num_subparts" : 3, + "height" : 4, + "colors" : [RED, WHITE], + "stroke_width" : 1, + "fill_opacity" : 1, + "propogate_style_to_family" : True, + } + def init_colors(self): + VMobject.init_colors(self) + self.gradient_highlight(*self.colors) + + def generate_points(self): + self.submobjects = self.get_order_n_self(self.order).submobjects + + def get_order_n_self(self, order): + if order == 0: + result = self.get_seed_shape() + else: + subparts = [ + self.get_order_n_self(order - 1) + for x in range(self.num_subparts) + ] + self.arrange_subparts(*subparts) + result = VGroup(*subparts) + + result.scale_to_fit_height(self.height) + result.center() + return result + + def get_seed_shape(self): + raise Exception("Not implemented") + + def arrange_subparts(self, *subparts): + raise Exception("Not implemented") + + +class Sierpinski(SelfSimilarFractal): + def get_seed_shape(self): + return Polygon( + RIGHT, np.sqrt(3)*UP, LEFT, + ) + + def arrange_subparts(self, *subparts): + tri1, tri2, tri3 = subparts + tri1.move_to(tri2.get_corner(DOWN+LEFT), UP) + tri3.move_to(tri2.get_corner(DOWN+RIGHT), UP) + + +class DiamondFractal(SelfSimilarFractal): + CONFIG = { + "num_subparts" : 4, + "height" : 6, + } + def get_seed_shape(self): + return RegularPolygon(n = 4) + + def arrange_subparts(self, *subparts): + # VGroup(*subparts).rotate(np.pi/4) + for part, vect in zip(subparts, compass_directions(start_vect = UP+RIGHT)): + part.next_to(ORIGIN, vect, buff = 0) + VGroup(*subparts).rotate(np.pi/4) + + +######## Space filling curves ############ + class SpaceFillingCurve(VMobject): CONFIG = { "radius" : 3, "order" : 5, "colors" : [RED, GREEN], + "monochromatic" : False, + "stroke_width" : 2, } def generate_points(self): points = self.get_anchor_points() - for triplet in zip(points, points[1:], points[2:]): - corner = VMobject() - corner.set_points_as_corners(triplet) - self.add(corner) + if self.monochromatic: + self.set_points_as_corners(points) + else: + for triplet in zip(points, points[1:], points[2:]): + corner = VMobject() + corner.set_points_as_corners(triplet) + self.add(corner) self.gradient_highlight(*self.colors) def get_anchor_points(self): @@ -244,11 +317,10 @@ class FlowSnake(LindenmayerCurve): LindenmayerCurve.__init__(self, **kwargs) self.rotate(-self.order*np.pi/9) -class Sierpinski(LindenmayerCurve): +class SierpinskiCurve(LindenmayerCurve): CONFIG = { - "start_color" : RED, - "end_color" : WHITE, - "axiom" : "A", + "colors" : [RED, WHITE], + "axiom" : "B", "rule" : { "A" : "+B-A-B+", "B" : "-A+B+A-", @@ -259,10 +331,9 @@ class Sierpinski(LindenmayerCurve): "angle" : -np.pi/3, } -class KochCurve(LindenmayerCurve): +class KochSnowFlake(LindenmayerCurve): CONFIG = { - "start_color" : BLUE_D, - "end_color" : WHITE, + "colors" : [BLUE_D, WHITE], "axiom" : "A--A--A--", "rule" : { "A" : "A+A--A+A" @@ -278,6 +349,12 @@ class KochCurve(LindenmayerCurve): self.scale_factor = 2*(1+np.cos(self.angle)) LindenmayerCurve.__init__(self, **kwargs) +class KochCurve(KochSnowFlake): + CONFIG = { + "axiom" : "A--" + } + + class StellarCurve(LindenmayerCurve): CONFIG = { diff --git a/topics/geometry.py b/topics/geometry.py index f586e21e..2f25b565 100644 --- a/topics/geometry.py +++ b/topics/geometry.py @@ -267,14 +267,14 @@ class Polygon(VMobject): def get_vertices(self): return self.get_anchors_and_handles()[0] -class RegularPolygon(VMobject): +class RegularPolygon(Polygon): CONFIG = { "start_angle" : 0 } def __init__(self, n = 3, **kwargs): digest_config(self, kwargs, locals()) start_vect = rotate_vector(RIGHT, self.start_angle) - vertices = compass_directions(n, start_angle) + vertices = compass_directions(n, start_vect) Polygon.__init__(self, *vertices, **kwargs)