# Making Gifs with Matplotlib and Recaman¶

This notebook is meant to be a tutorial on generating an animated gif using matplotlib. The subject to be animated is an implementation of the Racaman Sequence.

Helpful Resources:

```
import matplotlib.pyplot as plt
from matplotlib import patches
from matplotlib import animation
from matplotlib import rc
import os
from typing import List, Union
%matplotlib inline
from IPython.display import HTML
```

## Define the Sequence¶

First we need to create an implementation of the Recaman Sequence. Here the `set`

object is used
to maintain a list of values of `n`

, that have already been seen. While a set (`sequence`

) is a useful
object to check for member presence (due to hashing), a list (`recaman_series`

) is used to maintain sequence ordering.

```
def recaman(n: int) -> List[int]:
"""
Compute the values in the Recaman Sequence up to a N members
Args:
n (int): number of sequence members to compute
Returns:
recaman_series (list): The Recaman Sequence up to N members
"""
# Check to see if the sequence count is <= 0
if n <= 0 or not isinstance(n, int):
raise ValueError(f'Invalid Input, must be positive integer')
# initialize a set (monitor previous seen values) and a list (container for sequence results)
sequence = {0}
recaman_series = [0]
previous_value = 0
for i in range(1, n):
# check backwards direction since the sequence must go back if possible
current_value = previous_value - i
# If the value is negative or already exists.
if (current_value < 0 or current_value in sequence):
current_value = previous_value + i
sequence.add(current_value)
recaman_series.append(current_value)
previous_value = current_value # move the current value forward
return recaman_series
```

We want to grab a small set of the sequence to make sure it returns the expected results

```
N = 15
print(f'The first {N} members of the Recaman Sequence are: {recaman(N)}')
```

## Plotting¶

In order to plot the sequence in the semi-circle style, we need to first be able to calculate
the appropriate plot arcs. This is done using matplotlib's `patches`

library to generate the
appropriate arc segment for each member of the sequence.

```
def compute_arcs(sequence: List[int]) -> List:
"""
Compute each of the arc segments for each member of a Recaman Sequence
to be plotted using matplotlib
Args:
sequence (list): A Recaman Sequence
Returns:
arcs (list): A collection of matplotlib arc segments for
each member of the sequence
"""
arcs = []
for i in range(len(sequence) - 1):
# midway point should be between sequence members on the x-axis
arc_center = (0.5 * (sequence[i] + sequence[i + 1]), 0)
# arc heigh and width is proportional to the sequence element number
arc_width = abs(sequence[i + 1] - sequence[i])
arc_height = abs(sequence[i + 1] - sequence[i])
# slternate orientation of the arc (convex up or down)
arc_angle = (pow(-1, i + 1) + 1) / 2 * 180
# arc will always span 180 degrees
start_angle = 180
end_angle = 360
# create an arc segment
cur_arc = patches.Arc(
arc_center,
arc_width,
arc_height,
angle=arc_angle,
theta1=start_angle,
theta2=end_angle,
linewidth=1,
fill=False
)
arcs.append(cur_arc)
return arcs
```

With the ability to calculate the arc segment for each member of the sequence, a figure can be plotted

```
def plot_recaman(sequence: List[int]) -> None:
"""
Plot a semi-circular representation of a Recaman Sequence
Args:
sequence (list): A Recaman Sequence
Returns:
N/A
"""
# create the arcs associated with each sequence member
arcs = compute_arcs(sequence)
# create a figure and all each of the arc segments in order
fig, ax = plt.subplots(figsize=(9.5, 6))
for arc in arcs:
ax.add_patch(arc)
# set the plot limits to encompas the entire sequence
buffer_factor = 1.1 # buffer to give the figure breathing room
ax.set_xlim(-(buffer_factor - 1) * max(sequence), buffer_factor * max(sequence))
ax.set_ylim(-0.5 * buffer_factor * len(sequence), 0.5 * buffer_factor * len(sequence))
ax.set_aspect('equal')
ax.set_title(f'Recaman Sequence\nN=({len(sequence)})')
fig.tight_layout()
plt.show()
```

```
N = 25
plot_recaman(recaman(N))
```

## Animated Gif¶

To add some interest to the plot, we can animate it so that each arc segment is drawn for each member of the sequence.

```
def animate_recaman(sequence: List,
duration_sec: int = 5,
bitrate: int = 1000,
dpi: int = 100,
embedded: bool = False,
) -> Union[str, HTML]:
"""
Animate the plotting of a Recaman Sequence
Args:
sequence (list): Recaman sequence
duration_sec (int): duration of the animation, determines frames per second (fps)
bitrate (int): bits per second used to compress the output animation
dpi (int): dots per inch of the resulting animtaion
embedded (bool): flag to embed the animation as html5 (for use in a notebook)
Returns:
save_filename (str): The file name of the saved animation
html_video (HTML): A HTML5 encoded video of the animation
"""
fig, ax = plt.subplots(dpi=dpi)
edge_factor = 1.05 # set a 5% buffer to give the graph breathing room
# set the width of the plot to cover the sequence span while centering it
ax.set_xlim(-(edge_factor - 1) * max(sequence), edge_factor * max(sequence))
#set the height of the graph to cover the arcs
ax.set_ylim(-0.5 * edge_factor * len(sequence), 0.5 * edge_factor * len(sequence))
ax.set_aspect('equal')
ax.set_title(f"Recaman's Squence\nN={len(sequence)}")
# add a watermark to the figure
fig.text(0.95, 0.05, '@BrentonMallen',
fontsize=12, color='black',
ha='right', va='bottom', alpha=0.75)
# generate the arcs to plot
arcs = compute_arcs(sequence)
# calculate the fps based off desired duration
fps = len(sequence) // duration_sec
if embedded:
def animate(i):
"""
Function used to update the figure
"""
return ax.add_patch(i),
anim = animation.FuncAnimation(fig,
animate,
frames=arcs,
interval=100,
blit=True
)
plt.close() # prevent the static figure from displaying (the final frame)
html_video = HTML(anim.to_html5_video())
return html_video
GifWriter = animation.ImageMagickFileWriter(fps,
bitrate=bitrate
)
save_filename = f'recaman_{len(sequence)}.gif'
with GifWriter.saving(fig, save_filename, dpi=dpi):
for arc in arcs:
ax.add_patch(arc)
GifWriter.grab_frame()
return f'Animation saved to: {save_filename}'
```

## Embedding the Animation¶

To display the animation in this notebook, it has to be created as a HTML5 video
and embedded. To do this, the `FuncAnimation`

function is used. It differs from
the `ImageMagickFileWriter`

(used to save a Gif as a file) in that it requires
a set of frames and a function to definte the animation.

In this case, `frames`

in `FuncAnimation`

is the list of arcs generated (an iterable)
and the function `animate`

is used to add each arc to the figure as it updates for each
frame.

```
# specify that we want the animation to be displayed using html5 (html is default)
rc('animation', html='html5')
animate_recaman(recaman(100), embedded=True)
```

**Saving The Gif to File**

The `animate_recaman`

function is written in a way that allows for saving the gif
off to a file or converting it to an HTML5 video so that it can be embedded in
a notebook (as seen here)

**Note:**

The `GifWriter`

will generate temporary png files in the same directory
for each frame and then combine those to generate the gif. If for some
reason, the `GifWriter`

fails, those files will remain and have to be
manually deleted.