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.
Setup¶
Review the documentation before exploring the scenes. Docs
Create a new Colab notebook at colab.new and run the cells below.
The session will restart to apply changes.
%pip install mscene
import mscene
%mscene -l manim plugins
from mscene.manim import *
Objects¶
Functions for the animation scenes.
def fib_seq(n, a=0, b=1):
"""Returns a list of the first n terms, starting with the values a and b."""
seq = []
for _ in range(n):
seq.append(a)
a, b = b, a + b
return seq
def seq2str(seq):
"""Returns a string of numbers in sequence."""
seq_str = ", ".join(map(str, seq)) + ", ..."
return seq_str
def fib_spiral_mobj(seq, sf=1, add_dots=True):
"""Returns Mobjects for Fibonacci spiral.
Args:
seq (list[int]): List of Fibonacci numbers.
Returns:
VGroup: A group of Mobjects:
Square, Text, ArcBetweenPoints and Dot.
"""
sqr_clr, dot_clr, sprl_clr = [
ManimColor(hex) for hex in ("#214761", "#cfff04", "#04d9ff")
]
mobjects = VGroup()
squares = VGroup()
direction = (UP, RIGHT, DOWN, LEFT)
corner = (DL, UL)
dot_index = (0, 0, -1, -1)
for i, n in enumerate(seq):
square = Square(n * sf, stroke_width=6, color=sqr_clr).next_to(
squares, direction[i % 4], buff=0
)
dots = VGroup(
Dot(square.get_corner(corner[i % 2]), color=dot_clr),
Dot(square.get_corner(-corner[i % 2]), color=dot_clr),
)
spiral = ArcBetweenPoints(
dots[dot_index[i % 4]].get_center(),
dots[dot_index[i % 4] + 1].get_center(),
angle=-PI / 2,
color=sprl_clr,
stroke_width=6,
)
num = (
Text(f"{n}×{n}", fill_opacity=2 / 3)
.scale_to_fit_width(square.width * 0.5)
.move_to(square)
)
squares.add(square)
if add_dots:
vgroup = VGroup(square, num, spiral, dots)
else:
vgroup = VGroup(square, num, spiral)
vgroup[1:].set_z_index(1)
mobjects.add(vgroup)
mobjects.center()
return mobjects
def text_spiral_anim(text):
"""Text animation that spirals in and out."""
text_group = VGroup()
for t in text:
text_group.add(*t)
anim_in = SpiralIn(text_group, scale_factor=0)
anim_out = SpiralIn(
text_group,
scale_factor=0,
rate_func=lambda t: smooth(1 - t),
)
anim = (anim_in, anim_out)
return anim
Scenes¶
Scene One¶
%%manim -qm SceneOne
class SceneOne(Scene):
def construct(self):
size = 6
seq = fib_seq(size, 1)
frame_sizes = (config.frame_width, config.frame_height)
sf = frame_sizes[size % 2] / sum(seq[-2:]) * 0.75
mobj = fib_spiral_mobj(seq, sf)
text_width = config.frame_width / 2
text_group = VGroup(
*[Text(t).scale_to_fit_width(text_width) for t in ("Fibonacci", "Spiral")]
).arrange(DOWN)
text_anim_in, text_anim_out = text_spiral_anim(text_group)
spiral_anim_in = [
(FadeIn(i[0]), Write(i[1]), Create(i[2]), FadeIn(i[3])) for i in mobj
]
spiral_anim_out = [
(FadeOut(i[0]), Unwrite(i[1]), Uncreate(i[2]), FadeOut(i[3]))
for i in mobj[::-1]
]
self.play(text_anim_in)
self.wait()
self.play(text_anim_out)
self.wait()
self.play(AnimationGroup(*spiral_anim_in, lag_ratio=0.125))
self.wait(2.5)
self.play(AnimationGroup(*spiral_anim_out, lag_ratio=0.125))
self.wait(0.5)
Scene Two¶
%%manim -qm SceneTwo
class SceneTwo(Scene):
def construct(self):
size = 18
seq = fib_seq(size, 1)
frame_sizes = (config.frame_width, config.frame_height)
sf1 = frame_sizes[size % 2] / sum(seq[-2:]) * 0.75
sf2 = frame_sizes[1] * 0.5
mobj1 = fib_spiral_mobj(seq, sf1, add_dots=False)
mobj2 = fib_spiral_mobj(seq, sf2, add_dots=False)
point = mobj2.get_center() - mobj2[0].get_center()
mobj2.move_to(point)
mobj1.save_state()
self.add(mobj1)
self.wait()
self.play(Transform(mobj1, mobj2, run_time=3))
self.wait(2)
self.play(Restore(mobj1, run_time=3))
self.wait()
Scene Three¶
%%manim -qm SceneThree
class SceneThree(Scene):
def construct(self):
# sequence range: i to n
i, n = 1, 8
# i, n = 8,12 # try this!
print(fib_seq(n + 1)[i:])
seq = fib_seq(i, 1)
frame_sizes = (config.frame_width, config.frame_height)
sf = frame_sizes[i % 2] / sum(seq[-2:]) * 0.75
mobj = fib_spiral_mobj(seq, sf, add_dots=False)
anim_in = [(FadeIn(i[0]), Write(i[1]), Create(i[2])) for i in mobj]
self.play(*anim_in)
self.wait()
for j in range(i + 1, n + 1):
seq = fib_seq(j, 1)
sf = frame_sizes[j % 2] / sum(seq[-2:]) * 0.75
_mobj = fib_spiral_mobj(seq, sf, add_dots=False)
# transforms existing elements and then adds new ones
self.play(ReplacementTransform(mobj, _mobj[:-1]))
self.play(FadeIn(_mobj[-1][0]), Write(_mobj[-1][1]), Create(_mobj[-1][2]))
self.wait()
mobj = _mobj
anim_out = [(FadeOut(i[0]), Unwrite(i[1]), Uncreate(i[2])) for i in mobj]
self.wait()
self.play(*anim_out)
self.wait()