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.
This whole page is an optional extension of the core Gray-Scott activity.
Draw on the Field
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 imagedef 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
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
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