Fibonacci Spiral¶
There is a fascinating relation between the Fibonacci sequence and natural patterns. A remarkable example is the Fibonacci spiral, which is constructed by adding arcs of quarter circles with radii corresponding to Fibonacci numbers, making a beautiful mathematical pattern often observed throughout nature.
Open in ColabSetup¶
Visit colab.new to create a new Colab notebook.
Installation
The session will restart to apply changes.
%pip install -q mscene
import mscene
%mscene -l manim
Import
from mscene.manim import *
Objects¶
Functions to create objects for animation scenes.
def fseq(n, a=0, b=1):
"""Return the first n Fibonacci numbers starting with a and b."""
seq = []
for _ in range(n):
seq.append(a)
a, b = b, a + b
return seq
def fsmob(n, width=None, height=None):
"""VGroup of Mobjects for the Fibonacci spiral.
Args:
n (int): Number of first Fibonacci terms.
width (float | None): Width of the object (default: None).
height (float | None): Height of the object (default: None).
Returns:
VGroup of Square, Text, ArcBetweenPoints and Dot.
"""
sqr_color = ManimColor("#214761")
txt_color = ManimColor("#516572")
arc_color = ManimColor("#04d9ff")
dot_color = ManimColor("#cfff04")
seq = fseq(n, 1)
if width and not height:
scale = width / sum(seq[-2:])
elif height and not width:
scale = height / seq[-1]
else:
scale = 1
mobjects = VGroup()
squares = VGroup()
if len(seq) % 2:
angle = PI / 2
direction = (RIGHT, UP, LEFT, DOWN)
dot_index = (0, -1, -1, 0)
else:
angle = -PI / 2
direction = (UP, RIGHT, DOWN, LEFT)
dot_index = (0, 0, -1, -1)
corner = (DL, UL)
for i, t in enumerate(seq):
square = Square(t * scale, stroke_width=6, color=sqr_color).next_to(
squares,
direction[i % 4],
buff=0,
)
dots = VGroup(
Dot(square.get_corner(corner[i % 2]), color=dot_color),
Dot(square.get_corner(-corner[i % 2]), color=dot_color),
)
arc = ArcBetweenPoints(
dots[dot_index[i % 4]].get_center(),
dots[dot_index[i % 4] + 1].get_center(),
angle=angle,
color=arc_color,
stroke_width=6,
)
text = (
Text(f"{t}×{t}", color=txt_color)
.scale_to_fit_width(square.width * 0.5)
.move_to(square)
)
vgrp = VGroup(square, text, arc, dots)
vgrp[2:].set_z_index(1)
squares.add(square)
mobjects.add(vgrp)
mobjects.center()
return mobjects
def fsmob_anim(mob, mode="IN", lag_ratio=0.125, **kwargs):
"""Return an AnimationGroup for the Fibonacci spiral."""
if mode == "IN":
anim = [(FadeIn(i[0]), Write(i[1]), Create(i[2]), FadeIn(i[3])) for i in mob]
elif mode == "OUT":
anim = [
(FadeOut(i[0]), Unwrite(i[1]), Uncreate(i[2]), FadeOut(i[3]))
for i in mob[::-1]
]
else:
raise ValueError("mode must be 'IN' or 'OUT'")
return AnimationGroup(*anim, lag_ratio=lag_ratio, **kwargs)
Scenes¶
Scene One¶
%%manim -qm SceneOne
class SceneOne(Scene):
def construct(self):
seq = fseq(25)
width = config.frame_width * 3 / 4
seq_str = ", ".join(map(str, seq)) + ", ..."
title = Text("Fibonacci Sequence").scale_to_fit_width(width * 3 / 4)
text = MarkupText(seq_str, width=width, justify=True, font_size=130)
VGroup(title, text).arrange(DOWN, buff=3 / 4)
self.play(Write(title), Write(text, run_time=3))
self.wait(3)
Scene Two¶
%%manim -qm SceneTwo
class SceneTwo(Scene):
def construct(self):
width = config.frame_width * 3 / 4
mob = fsmob(6, width=width)
self.play(fsmob_anim(mob))
self.wait(2.5)
self.play(fsmob_anim(mob, mode="OUT"))
self.wait(0.5)
Scene Three¶
%%manim -qm SceneThree
class SceneThree(Scene):
def construct(self):
width = config.frame_width * 3 / 4
terms = [4, 8, 12]
term = None
mob = None
for n in terms:
_mob = fsmob(n, width)
if mob is None:
self.play(fsmob_anim(_mob))
else:
i = term if term < n else -1
self.play(ReplacementTransform(mob, _mob[:i]))
self.wait(0.5)
self.play(fsmob_anim(_mob[i:]))
mob = _mob
term = n
self.wait(1.5)
self.play(fsmob_anim(mob, mode="OUT", lag_ratio=0))
self.wait(0.5)
Scene Four¶
%%manim -qm SceneFour
class SceneFour(Scene):
def construct(self):
n = 12
width = config.frame_width * 3 / 4
mob = fsmob(n, width)
mob.save_state()
width *= sum(fseq(n)[-2:]) * 3 / 4
_mob = fsmob(n, width)
_mob.shift(-_mob[0].get_center())
self.add(mob)
self.wait(0.5)
self.play(Transform(mob, _mob, run_time=6))
self.wait(2)
self.play(Restore(mob, run_time=4))
self.wait(0.5)