Written by Yair Mau. Check out my webpage for more tutorials: http://www.yairmau.com/

Snell's law of refraction can be understood in this example, where the lifeguard wants to minimize the time it takes to get to the drowning person.

In [1]:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
In [2]:
# http://wiki.scipy.org/Cookbook/Matplotlib/LaTeX_Examples
# this is a latex constant, don't change it.
pts_per_inch = 72.27
# write "\the\textwidth" (or "\showthe\columnwidth" for a 2 collumn text)
text_width_in_pts = 252.0
# inside a figure environment in latex, the result will be on the
# dvi/pdf next to the figure. See url above.
text_width_in_inches = text_width_in_pts / pts_per_inch
# make rectangles with a nice proportion
golden_ratio = 0.618
# figure.png or figure.eps will be intentionally larger, because it is prettier
inverse_latex_scale = 2
# when compiling latex code, use
# \includegraphics[scale=(1/inverse_latex_scale)]{figure}
# we want the figure to occupy 2/3 (for example) of the text width
fig_proportion = (3.0 / 3.0)
csize = inverse_latex_scale * fig_proportion * text_width_in_inches
# always 1.0 on the first argument
fig_size = (1.0 * csize,golden_ratio * csize)
# find out the fontsize of your latex text, and put it here
text_size = inverse_latex_scale * 12
tick_size = inverse_latex_scale * 8
# learn how to configure:
# http://matplotlib.sourceforge.net/users/customizing.html
params = {'backend': 'ps',
          'axes.labelsize': text_size,
          'text.fontsize': text_size,
          'legend.fontsize': tick_size,
          'legend.handlelength': 2.5,
          'legend.borderaxespad': 0,
          'xtick.labelsize': tick_size,
          'ytick.labelsize': tick_size,
          'font.family': 'serif',
          'font.size': text_size,
          # Times, Palatino, New Century Schoolbook,
          # Bookman, Computer Modern Roman
          'font.serif': ['Times'],
          'ps.usedistiller': 'xpdf',
          'text.usetex': True,
          'figure.figsize': fig_size,
          # include here any neede package for latex
          'text.latex.preamble': [r'\usepackage{amsmath}',
                                  ],
          }
plt.rcParams.update(params)
plt.ion()
plt.clf()
# figsize accepts only inches.
fig = plt.figure(1, figsize=fig_size)
fig.subplots_adjust(left=0.0, right=1.0, top=1.0, bottom=0.0,
                    hspace=0.02, wspace=0.02)
ax = fig.add_subplot(111)
/Users/yairmau/anaconda/lib/python2.7/site-packages/matplotlib/__init__.py:872: UserWarning: text.fontsize is deprecated and replaced with font.size; please use the latter.
  warnings.warn(self.msg_depr % (key, alt_key))
In [3]:
origin = [0, 0]
lifeguard = [-3, -2]
drowning = [2, 3]

ax.set_xticks([])
ax.set_yticks([])
xlim = [-4, 4]
ylim = [-4, 4]
ax.axis([xlim[0], xlim[1], ylim[0], ylim[1]])

##### drowning #####
# line
ax.plot([origin[0], drowning[0]], [origin[1], drowning[1]],
        color="black", lw=2)
# diamond
ax.plot(drowning[0], drowning[1], "D", markerfacecolor="black", markersize=10,
        markeredgewidth=3, color="black")
# explanation
ax.text(drowning[0], drowning[1] - 0.3, r"drowning", verticalalignment="top")
ax.text(drowning[0], drowning[1] - 0.8, r"person", verticalalignment="top")

##### lifeguard #####
# line
ax.plot([origin[0], lifeguard[0]], [origin[1], lifeguard[1]],
        color="black", lw=2)
# circle
ax.plot(lifeguard[0], lifeguard[1], "o", markerfacecolor="black",
        markersize=10, markeredgewidth=3, color="black")
# explanation
ax.text(lifeguard[0], lifeguard[1] - 0.3, r"lifeguard",
        verticalalignment="top")
Out[3]:
<matplotlib.text.Text at 0x10b208d90>
In [4]:
# background colors
sand = matplotlib.patches.Rectangle([xlim[0], ylim[0]],
                                    (xlim[1] - xlim[0]),
                                    (ylim[1] - ylim[0]) / 2.0,
                                    color="yellow", alpha=0.6)
ax.add_patch(sand)
sea = matplotlib.patches.Rectangle([xlim[0], 0],
                                   (xlim[1] - xlim[0]),
                                   (ylim[1] - ylim[0]) / 2.0,
                                   color="blue", alpha=0.4)
ax.add_patch(sea)
###### sand #####
ax.text(0.95, 0.05, r"(1) sand",
        transform=ax.transAxes, horizontalalignment='right')
###### sea #####
ax.text(0.95, 0.50, r"(2) sea",
        transform=ax.transAxes, horizontalalignment='right',
        verticalalignment="bottom")
Out[4]:
<matplotlib.text.Text at 0x10b2348d0>
In [5]:
# zeroed spines
# http://matplotlib.org/examples/pylab_examples/spine_placement_demo.html
ax.spines['left'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_color('none')
ax.spines['left'].set_smart_bounds(False)
ax.spines['bottom'].set_smart_bounds(False)
# ax.xaxis.set_ticks_position('bottom')
# ax.yaxis.set_ticks_position('left')
ax.set_xticks([])
ax.set_yticks([])
Out[5]:
[]
In [6]:
# annotations

# h_1
ax.annotate("",
            xy=(lifeguard[0] - 0.3, lifeguard[1]), xycoords='data',
            xytext=(lifeguard[0] - 0.3, 0), textcoords='data',
            size=tick_size,
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="arc3"),
            )
ax.text(lifeguard[0] - 0.2, lifeguard[1] / 2.0, r"$h_1$",
        verticalalignment="center", horizontalalignment="left")
# h_2
ax.annotate("",
            xy=(lifeguard[0] - 0.3, drowning[1]), xycoords='data',
            xytext=(lifeguard[0] - 0.3, 0), textcoords='data',
            size=tick_size,
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="arc3"),
            )
ax.text(lifeguard[0] - 0.2, drowning[1] / 2.0, r"$h_2$",
        verticalalignment="center", horizontalalignment="left")
# L
ax.annotate("",
            xy=(lifeguard[0], drowning[1] + 0.3), xycoords='data',
            xytext=(drowning[0], drowning[1] + 0.3), textcoords='data',
            size=tick_size,
            arrowprops=dict(arrowstyle="<->",
                            shrinkA=0, shrinkB=0,
                            connectionstyle="arc3"),
            )
ax.text((lifeguard[1] - lifeguard[0]) / 2.0, drowning[1] + 0.3, r"$L$",
        verticalalignment="bottom", horizontalalignment="left")

# l1
ax.text(lifeguard[0] / 2.0, 1.10 * lifeguard[1] / 2.0, r"$\ell_1$",
        verticalalignment="top", horizontalalignment="left")
# l2
ax.text(drowning[0] / 2.0, 0.95 * drowning[1] / 2.0, r"$\ell_2$",
        verticalalignment="top", horizontalalignment="left")

# theta_1
ax.annotate("",
            xy=(0, 0.5 * lifeguard[1]), xycoords='data',
            xytext=(0.2 * lifeguard[0], 0.2 * lifeguard[1]), textcoords='data',
            size=tick_size,
            arrowprops=dict(arrowstyle="-", lw=2,
                            connectionstyle="angle3,angleA=-60,angleB=0"),
            )
ax.text(0.1 * lifeguard[0], 0.5 * lifeguard[1], r"$\theta_1$",
        verticalalignment="top", horizontalalignment="right")

# theta_2
ax.annotate("",
            xy=(0, 0.3 * drowning[1]), xycoords='data',
            xytext=(0.2 * drowning[0], 0.2 * drowning[1]), textcoords='data',
            size=tick_size,
            arrowprops=dict(arrowstyle="-", lw=2,
                            connectionstyle="angle3,angleA=120,angleB=0"),
            )
ax.text(0.1 * drowning[0], 0.5 * drowning[1], r"$\theta_2$",
        verticalalignment="top", horizontalalignment="left")
Out[6]:
<matplotlib.text.Text at 0x10b1e6110>
In [7]:
fig.savefig("./figures/sea-sand.png",resolution=300)
fig.show()