Skip to content

Commit fd746a7

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 454c624 + 3a85213 commit fd746a7

File tree

3 files changed

+49
-22
lines changed

3 files changed

+49
-22
lines changed

mesa/experimental/cell_space/voronoi.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ def __init__(
186186
random: Random | None = None,
187187
cell_klass: type[Cell] = Cell,
188188
capacity_function: callable = round_float,
189-
cell_coloring_property: str | None = None,
190189
) -> None:
191190
"""A Voronoi Tessellation Grid.
192191
@@ -200,7 +199,7 @@ def __init__(
200199
random (Random): random number generator
201200
cell_klass (type[Cell]): type of cell class
202201
capacity_function (Callable): function to compute (int) capacity according to (float) area
203-
cell_coloring_property (str): voronoi visualization polygon fill property
202+
204203
"""
205204
super().__init__(capacity=capacity, random=random, cell_klass=cell_klass)
206205
self.centroids_coordinates = centroids_coordinates
@@ -215,7 +214,6 @@ def __init__(
215214
self.triangulation = None
216215
self.voronoi_coordinates = None
217216
self.capacity_function = capacity_function
218-
self.cell_coloring_property = cell_coloring_property
219217

220218
self._connect_cells()
221219
self._build_cell_polygons()
@@ -266,4 +264,3 @@ def _build_cell_polygons(self):
266264
polygon_area = self._compute_polygon_area(polygon)
267265
self._cells[region].properties["area"] = polygon_area
268266
self._cells[region].capacity = self.capacity_function(polygon_area)
269-
self._cells[region].properties[self.cell_coloring_property] = 0

mesa/visualization/mpl_space_drawing.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from matplotlib.cm import ScalarMappable
2121
from matplotlib.collections import PatchCollection
2222
from matplotlib.colors import LinearSegmentedColormap, Normalize, to_rgba
23-
from matplotlib.patches import RegularPolygon
23+
from matplotlib.patches import Polygon, RegularPolygon
2424

2525
import mesa
2626
from mesa.experimental.cell_space import (
@@ -501,14 +501,19 @@ def draw_continuous_space(
501501

502502

503503
def draw_voronoi_grid(
504-
space: VoronoiGrid, agent_portrayal: Callable, ax: Axes | None = None, **kwargs
504+
space: VoronoiGrid,
505+
agent_portrayal: Callable,
506+
ax: Axes | None = None,
507+
draw_grid: bool = True,
508+
**kwargs,
505509
):
506510
"""Visualize a voronoi grid.
507511
508512
Args:
509513
space: the space to visualize
510514
agent_portrayal: a callable that is called with the agent and returns a dict
511515
ax: a Matplotlib Axes instance. If none is provided a new figure and ax will be created using plt.subplots
516+
draw_grid: whether to draw the grid or not
512517
kwargs: additional keyword arguments passed to ax.scatter
513518
514519
Returns:
@@ -541,16 +546,18 @@ def draw_voronoi_grid(
541546

542547
_scatter(ax, arguments, **kwargs)
543548

544-
for cell in space.all_cells:
545-
polygon = cell.properties["polygon"]
546-
ax.fill(
547-
*zip(*polygon),
548-
alpha=min(1, cell.properties[space.cell_coloring_property]),
549-
c="red",
550-
zorder=0,
551-
) # Plot filled polygon
552-
ax.plot(*zip(*polygon), color="black") # Plot polygon edges in black
549+
def setup_voroinoimesh(cells):
550+
patches = []
551+
for cell in cells:
552+
patch = Polygon(cell.properties["polygon"])
553+
patches.append(patch)
554+
mesh = PatchCollection(
555+
patches, edgecolor="k", facecolor=(1, 1, 1, 0), linestyle="dotted", lw=1
556+
)
557+
return mesh
553558

559+
if draw_grid:
560+
ax.add_collection(setup_voroinoimesh(space.all_cells.cells))
554561
return ax
555562

556563

mesa/visualization/solara_viz.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def SolaraViz(
5252
| Literal["default"] = "default",
5353
*,
5454
play_interval: int = 100,
55+
render_interval: int = 1,
5556
simulator: Simulator | None = None,
5657
model_params=None,
5758
name: str | None = None,
@@ -72,6 +73,8 @@ def SolaraViz(
7273
Defaults to "default", which uses the default Altair space visualization.
7374
play_interval (int, optional): Interval for playing the model steps in milliseconds.
7475
This controls the speed of the model's automatic stepping. Defaults to 100 ms.
76+
render_interval (int, optional): Controls how often plots are updated during a simulation,
77+
allowing users to skip intermediate steps and update graphs less frequently.
7578
simulator: A simulator that controls the model (optional)
7679
model_params (dict, optional): Parameters for (re-)instantiating a model.
7780
Can include user-adjustable parameters and fixed parameters. Defaults to None.
@@ -90,6 +93,8 @@ def SolaraViz(
9093
model instance is provided, it will be converted to a reactive model using `solara.use_reactive`.
9194
- The `play_interval` argument controls the speed of the model's automatic stepping. A lower
9295
value results in faster stepping, while a higher value results in slower stepping.
96+
- The `render_interval` argument determines how often plots are updated during simulation. Higher values
97+
reduce update frequency,resulting in faster execution.
9398
"""
9499
if components == "default":
95100
components = [components_altair.make_altair_space()]
@@ -103,7 +108,7 @@ def SolaraViz(
103108
# set up reactive model_parameters shared by ModelCreator and ModelController
104109
reactive_model_parameters = solara.use_reactive({})
105110
reactive_play_interval = solara.use_reactive(play_interval)
106-
111+
reactive_render_interval = solara.use_reactive(render_interval)
107112
with solara.AppBar():
108113
solara.AppBarTitle(name if name else model.value.__class__.__name__)
109114

@@ -117,18 +122,28 @@ def SolaraViz(
117122
max=500,
118123
step=10,
119124
)
125+
solara.SliderInt(
126+
label="Render Interval (steps)",
127+
value=reactive_render_interval,
128+
on_value=lambda v: reactive_render_interval.set(v),
129+
min=1,
130+
max=100,
131+
step=2,
132+
)
120133
if not isinstance(simulator, Simulator):
121134
ModelController(
122135
model,
123136
model_parameters=reactive_model_parameters,
124137
play_interval=reactive_play_interval,
138+
render_interval=reactive_render_interval,
125139
)
126140
else:
127141
SimulatorController(
128142
model,
129143
simulator,
130144
model_parameters=reactive_model_parameters,
131145
play_interval=reactive_play_interval,
146+
render_interval=reactive_render_interval,
132147
)
133148
with solara.Card("Model Parameters"):
134149
ModelCreator(
@@ -189,14 +204,15 @@ def ModelController(
189204
*,
190205
model_parameters: dict | solara.Reactive[dict] = None,
191206
play_interval: int | solara.Reactive[int] = 100,
207+
render_interval: int | solara.Reactive[int] = 1,
192208
):
193209
"""Create controls for model execution (step, play, pause, reset).
194210
195211
Args:
196212
model: Reactive model instance
197213
model_parameters: Reactive parameters for (re-)instantiating a model.
198214
play_interval: Interval for playing the model steps in milliseconds.
199-
215+
render_interval: Controls how often the plots are updated during simulation steps.Higher value reduce update frequency.
200216
"""
201217
playing = solara.use_reactive(False)
202218
running = solara.use_reactive(True)
@@ -215,9 +231,12 @@ async def step():
215231

216232
@function_logger(__name__)
217233
def do_step():
218-
"""Advance the model by one step."""
219-
model.value.step()
234+
"""Advance the model by the number of steps specified by the render_interval slider."""
235+
for _ in range(render_interval.value):
236+
model.value.step()
237+
220238
running.value = model.value.running
239+
221240
force_update()
222241

223242
@function_logger(__name__)
@@ -259,6 +278,7 @@ def SimulatorController(
259278
*,
260279
model_parameters: dict | solara.Reactive[dict] = None,
261280
play_interval: int | solara.Reactive[int] = 100,
281+
render_interval: int | solara.Reactive[int] = 1,
262282
):
263283
"""Create controls for model execution (step, play, pause, reset).
264284
@@ -267,7 +287,11 @@ def SimulatorController(
267287
simulator: Simulator instance
268288
model_parameters: Reactive parameters for (re-)instantiating a model.
269289
play_interval: Interval for playing the model steps in milliseconds.
290+
render_interval: Controls how often the plots are updated during simulation steps.Higher values reduce update frequency.
270291
292+
Notes:
293+
The `step button` increments the step by the value specified in the `render_interval` slider.
294+
This behavior ensures synchronization between simulation steps and plot updates.
271295
"""
272296
playing = solara.use_reactive(False)
273297
running = solara.use_reactive(True)
@@ -285,8 +309,8 @@ async def step():
285309
)
286310

287311
def do_step():
288-
"""Advance the model by one step."""
289-
simulator.run_for(1)
312+
"""Advance the model by the number of steps specified by the render_interval slider."""
313+
simulator.run_for(render_interval.value)
290314
running.value = model.value.running
291315
force_update()
292316

@@ -390,7 +414,6 @@ def ModelCreator(
390414
or are dictionaries containing parameter details such as type, value, min, and max.
391415
- The `seed` argument ensures reproducibility by setting the initial seed for the model's random number generator.
392416
- The component provides an interface for adjusting user-defined parameters and reseeding the model.
393-
394417
"""
395418
if model_parameters is None:
396419
model_parameters = {}

0 commit comments

Comments
 (0)