Gray-Scott: Interaction

Module 4: Space and Pattern Formation

Start from the animation on Gray-Scott Animation. Now you turn it into a small laboratory where you can inject perturbations, pause the evolution, and change parameters while the simulation is running.

This is the same pattern as in the interactive pages of Module 1: keep the model update fixed, then wrap it with event handlers and controls. The handlers below use nonlocal, so they must live inside one function that owns uv, pause, and the parameters, as in amlab.pdes_2d.gray_scott.run_simulation.

Optional extension

This whole page is an optional extension of the core Gray-Scott activity.

Draw on the Field

Exercise

Left click should add a local perturbation and right click should reset a small patch to the base state.

from matplotlib.backend_bases import MouseEvent

def update_uv(event: MouseEvent, r: int = 3):
    # TODO: ignore clicks outside the axes
    # TODO: convert the mouse location into integer grid indices

    # TODO: left click should add a local perturbation near (u, v) = (0.5, 0.5)
    # TODO: right click should reset the patch to (u, v) = (1.0, 0.0)

    # TODO: update the selected patch in uv and refresh the image
def update_uv(event: MouseEvent, r: int = 3):
    if event.inaxes != ax_uv:
        return
    if event.xdata is None or event.ydata is None:
        return

    x = int(event.xdata)
    y = int(event.ydata)

    if event.button == 1:
        u_new = 0.5 * (1 + 0.1 * np.random.randn())
        v_new = 0.5 * (1 + 0.1 * np.random.randn())
    elif event.button == 3:
        u_new = 1.0
        v_new = 0.0
    else:
        return

    uv[0, y - r : y + r, x - r : x + r] = u_new
    uv[1, y - r : y + r, x - r : x + r] = v_new
    im.set_array(uv[1])

Allow Continuous Drawing

Exercise

Use three event handlers so the perturbation follows the mouse while the button is pressed.

from matplotlib.backend_bases import MouseEvent

drawing = False

def on_click(event: MouseEvent):
    # TODO: start drawing and call update_uv(event)

def on_release(event: MouseEvent):
    # TODO: stop drawing

def on_motion(event: MouseEvent):
    # TODO: if drawing is active, update the field

fig.canvas.mpl_connect("button_press_event", on_click)
fig.canvas.mpl_connect("button_release_event", on_release)
fig.canvas.mpl_connect("motion_notify_event", on_motion)
def on_click(event: MouseEvent):
    nonlocal drawing
    drawing = True
    update_uv(event)


def on_release(event: MouseEvent):
    nonlocal drawing
    drawing = False


def on_motion(event: MouseEvent):
    if drawing:
        update_uv(event)

Pause and Resume with the Keyboard

Exercise

Toggle a pause flag when the user presses the space bar.

from matplotlib.backend_bases import KeyEvent

pause = False

def on_keypress(event: KeyEvent):
    # TODO: toggle pause when the space bar is pressed

fig.canvas.mpl_connect("key_press_event", on_keypress)
def on_keypress(event: KeyEvent):
    nonlocal pause
    if event.key == " ":
        pause ^= True