Nanocartography: Planning for success in analytical electron microscopy

Superellipse

Article - Nanocartography Interactivity - Superellipse figure v2 - Colin update

# imports for widgets
from IPython.display import HTML, display, Math
import ipywidgets as widgets
from ipywidgets import FloatLogSlider, FloatSlider, Layout, HBox, VBox, Label
 # from IPython.display import display, Math


# importing required libraries
import numpy as np  
import matplotlib.pyplot as plt

# for creating a responsive plot
%matplotlib widget
# Define super ellipse function

def calc_superellipse(
    theta,
    r,
    a,
    b,
    ):
    """
    Super ellipse function definition. Inputs are angle theta, shape factor r, and limits (a,b).
    Returns alpha and beta for all 4 quadrants.
    """
    pos_alpha = a * (np.cos(theta)**(2/r))
    pos_beta  = b * (np.sin(theta)**(2/r))

    alpha = np.hstack((
        pos_alpha,
        -np.flip(pos_alpha),
        -pos_alpha,
        np.flip(pos_alpha),
    ))
    beta = np.hstack((
        pos_beta,
        np.flip(pos_beta),
        -pos_beta,
        -np.flip(pos_beta),
    ))

    return alpha,beta
# Inputs
r_init = 3.3
a_init = 36.0
b_init = 32.0
ab_range = (5, 45)
ab_plot_range = 50
r_range = (1,10)
step = 0.01

theta = np.linspace(
    0.0,
    np.pi/2,
    90,
    endpoint=False)


fontsize = 18
dpi = 72

# initialize figure
plt.close('all')
plt.ioff()
fig = plt.figure(figsize=(440/dpi, 400/dpi), dpi=dpi)
ax = fig.add_axes((0.16, 0.12, 0.80, 0.86))

fig.canvas.resizable = False
fig.canvas.header_visible = False
fig.canvas.footer_visible = False
fig.canvas.toolbar_visible = False
fig.canvas.layout.width = '470px'
fig.canvas.toolbar_position = 'bottom'

# Transparent figure
fig.set_frameon(False)
ax.patch.set_visible(False)

ax.set_xlim((-ab_plot_range,ab_plot_range))
ax.set_ylim((-ab_plot_range,ab_plot_range))
ax.set_xlabel(r'$\alpha$ (degrees)', fontsize = fontsize)
ax.set_ylabel(r'$\beta$ (degrees)', fontsize = fontsize)
ax.tick_params(axis='both', which='major', labelsize = fontsize - 4)

c = (0.4,0.4,0.4)
ax.spines['bottom'].set_color(c)
ax.spines['top'].set_color(c) 
ax.spines['right'].set_color(c)
ax.spines['left'].set_color(c)
ax.grid(which='major', color=c, linestyle='-', alpha=0.7)
ax.grid(which='minor', color=c, linestyle='-', alpha=0.4)
ax.minorticks_on()

ax.xaxis.label.set_color(c)
ax.yaxis.label.set_color(c)
ax.tick_params(axis='both', colors=c)

# plot the superellipse
rab = [r_init, a_init, b_init]
alpha,beta = calc_superellipse(
    theta,
    rab[0],
    rab[1],
    rab[2],
)
superellipse = ax.fill(
    alpha,
    beta,
    edgecolor = (1,0,0,1),
    facecolor = (1,0,0,0.3),
    lw = 3,
)
# superellipse = superellipse_outer.get_paths()[0]

# dummy = ax.fill_between(t, 0, f(t, amp_slider.val, freq_slider.val), alpha=0)
#         dp = dummy.get_paths()[0]
#         dummy.remove()
#         #update the vertices of the PolyCollection
#         fill.set_paths([dp.vertices])

# update functions
def update_r(change):
    rab[0] = change['new']
    alpha,beta = calc_superellipse(
        theta,
        rab[0],
        rab[1],
        rab[2],
    )
    superellipse[0].set_xy(np.vstack((alpha,beta)).T)
    fig.canvas.draw_idle()
    return None
def update_a(change):
    rab[1] = change['new']
    alpha,beta = calc_superellipse(
        theta,
        rab[0],
        rab[1],
        rab[2],
    )
    superellipse[0].set_xy(np.vstack((alpha,beta)).T)
    fig.canvas.draw_idle()
    return None
def update_b(change):
    rab[2] = change['new']
    alpha,beta = calc_superellipse(
        theta,
        rab[0],
        rab[1],
        rab[2],
    )
    superellipse[0].set_xy(np.vstack((alpha,beta)).T)
    fig.canvas.draw_idle()
    return None


# Add the interactive widgets
slider_r = FloatLogSlider(
    value=rab[0],
    min=np.log10(r_range[0]),
    max=np.log10(r_range[1]),
    step = step,
    continuous_update=True,
    orientation='horizontal',
    indent=True,
    layout=Layout(width='200px'),
    readout_format='.1f',
)
slider_r.observe(update_r,names='value')
slider_a = FloatSlider(
    value=rab[1],
    min=ab_range[0],
    max=ab_range[1],
    continuous_update=True,
    orientation='horizontal',
    indent=True,
    layout=Layout(width='200px'),
    readout_format='.1f',
)
slider_a.observe(update_a,names='value')
slider_b = FloatSlider(
    value=rab[2],
    min=ab_range[0],
    max=ab_range[1],
    continuous_update=True,
    orientation='horizontal',
    indent=True,
    layout=Layout(width='200px'),
    readout_format='.1f',
)
slider_b.observe(update_b,names='value')


# widgets.Text(
#     value='Hello World',
#     placeholder='Type something',
#     description='String:',
#     disabled=False   
# )
# Controls box
controls_vbox = VBox([
    Label(
        value = r"Shape Factor $$\,\,r$$",
        style={'font_weight': 'bold'},
        layout=Layout(width='200px')
    ),
    slider_r,
    Label(
        value = r"Tilt Limit $$\,\,\alpha$$",
        style={'font_weight': 'bold'},
        layout=Layout(width='200px')
    ),
    slider_a,
    Label(
        value = r"Tilt Limit $$\,\,\beta$$",
        style={'font_weight': 'bold'},
        layout=Layout(width='200px')
    ),
    slider_b,
],layout=Layout(width='220px'))


# Assemble widget
visualization_layout = Layout(
    display='flex',
    flex_flow='row',
    align_items='center',
    width='680px'
)
display(
    HBox(
        [
            fig.canvas,
            controls_vbox
        ],
        layout=visualization_layout
        )
)