Source code for modelly.viz.mpl.continuous
"""Draw continuous spaces with their agents."""
from collections.abc import Callable
from matplotlib import pyplot as plt
from matplotlib.axes import Axes
from modelly.space.continuous import ContinuousSpace
from modelly.viz.mpl.agent_data import collect_agent_data
from modelly.viz.mpl.grids import MARKER_SPAN_POINTS, PADDING_DIVISOR
from modelly.viz.mpl.scatter import _scatter
# Dash pattern (offset, (on, off)) of the border of a toroidal space.
TORUS_BORDER_DASH = (0, (5, 10))
BORDER_LINEWIDTH = 1.5
[docs]
def draw_continuous_space(
space: ContinuousSpace, agent_portrayal: Callable, ax: Axes | None = None, **kwargs
):
"""Visualize a continuous space.
Args:
space: the space to visualize
agent_portrayal: a callable that is called with the agent and returns a AgentPortrayalStyle
ax: a Matplotlib Axes instance. If none is provided a new figure and ax will be created using plt.subplots
kwargs: additional keyword arguments passed to ax.scatter
Returns:
Returns the Axes object with the plot drawn onto it.
``agent_portrayal`` is called with an agent and should return a AgentPortrayalStyle. Valid fields in this object are "color",
"size", "marker", "zorder", alpha, linewidths, and edgecolors. Other field are ignored and will result in a user warning.
"""
if ax is None:
_, ax = plt.subplots()
# space related setup
width = space.x_max - space.x_min
x_padding = width / PADDING_DIVISOR
height = space.y_max - space.y_min
y_padding = height / PADDING_DIVISOR
# gather agent data
s_default = (MARKER_SPAN_POINTS / max(width, height)) ** 2
arguments = collect_agent_data(space, agent_portrayal, default_size=s_default)
# further visual styling
_style_border(ax, space.torus)
ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding)
ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding)
# plot the agents
_scatter(ax, arguments, **kwargs)
return ax
def _style_border(ax, torus):
"""Draw the axes frame solid, or dashed for a toroidal space."""
border_style = "solid" if not torus else TORUS_BORDER_DASH
for spine in ax.spines.values():
spine.set_linewidth(BORDER_LINEWIDTH)
spine.set_color("black")
spine.set_linestyle(border_style)