Mesa 3.1.5__py3-none-any.whl → 3.2.0.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of Mesa might be problematic. Click here for more details.
- mesa/__init__.py +3 -1
- mesa/agent.py +20 -5
- mesa/discrete_space/__init__.py +50 -0
- mesa/{experimental/cell_space → discrete_space}/cell.py +29 -10
- mesa/{experimental/cell_space → discrete_space}/cell_agent.py +1 -1
- mesa/{experimental/cell_space → discrete_space}/cell_collection.py +3 -3
- mesa/{experimental/cell_space → discrete_space}/discrete_space.py +65 -3
- mesa/{experimental/cell_space → discrete_space}/grid.py +2 -2
- mesa/{experimental/cell_space → discrete_space}/network.py +22 -2
- mesa/{experimental/cell_space → discrete_space}/property_layer.py +1 -10
- mesa/{experimental/cell_space → discrete_space}/voronoi.py +2 -2
- mesa/examples/advanced/epstein_civil_violence/agents.py +1 -1
- mesa/examples/advanced/epstein_civil_violence/model.py +1 -1
- mesa/examples/advanced/pd_grid/agents.py +1 -1
- mesa/examples/advanced/pd_grid/model.py +1 -1
- mesa/examples/advanced/sugarscape_g1mt/agents.py +1 -1
- mesa/examples/advanced/sugarscape_g1mt/model.py +2 -2
- mesa/examples/advanced/wolf_sheep/agents.py +1 -1
- mesa/examples/advanced/wolf_sheep/app.py +2 -1
- mesa/examples/advanced/wolf_sheep/model.py +1 -1
- mesa/examples/basic/boid_flockers/agents.py +1 -0
- mesa/examples/basic/boid_flockers/app.py +17 -2
- mesa/examples/basic/boid_flockers/model.py +12 -0
- mesa/examples/basic/boltzmann_wealth_model/agents.py +6 -11
- mesa/examples/basic/boltzmann_wealth_model/app.py +2 -2
- mesa/examples/basic/boltzmann_wealth_model/model.py +7 -11
- mesa/examples/basic/conways_game_of_life/agents.py +13 -5
- mesa/examples/basic/conways_game_of_life/model.py +10 -7
- mesa/examples/basic/schelling/agents.py +13 -8
- mesa/examples/basic/schelling/model.py +6 -9
- mesa/examples/basic/virus_on_network/agents.py +13 -17
- mesa/examples/basic/virus_on_network/model.py +20 -24
- mesa/experimental/__init__.py +2 -2
- mesa/experimental/cell_space/__init__.py +18 -8
- mesa/space.py +1 -12
- mesa/visualization/__init__.py +2 -0
- mesa/visualization/command_console.py +482 -0
- mesa/visualization/components/altair_components.py +276 -16
- mesa/visualization/mpl_space_drawing.py +17 -9
- mesa/visualization/solara_viz.py +150 -21
- {mesa-3.1.5.dist-info → mesa-3.2.0.dev0.dist-info}/METADATA +12 -8
- {mesa-3.1.5.dist-info → mesa-3.2.0.dev0.dist-info}/RECORD +45 -43
- {mesa-3.1.5.dist-info → mesa-3.2.0.dev0.dist-info}/WHEEL +0 -0
- {mesa-3.1.5.dist-info → mesa-3.2.0.dev0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.1.5.dist-info → mesa-3.2.0.dev0.dist-info}/licenses/NOTICE +0 -0
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
import warnings
|
|
4
4
|
|
|
5
5
|
import altair as alt
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
6
8
|
import solara
|
|
9
|
+
from matplotlib.colors import to_rgb
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
from mesa.
|
|
11
|
+
import mesa
|
|
12
|
+
from mesa.discrete_space import DiscreteSpace, Grid
|
|
13
|
+
from mesa.space import ContinuousSpace, PropertyLayer, _Grid
|
|
10
14
|
from mesa.visualization.utils import update_counter
|
|
11
15
|
|
|
12
16
|
|
|
@@ -20,13 +24,16 @@ def make_space_altair(*args, **kwargs): # noqa: D103
|
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
def make_altair_space(
|
|
23
|
-
agent_portrayal,
|
|
27
|
+
agent_portrayal,
|
|
28
|
+
propertylayer_portrayal=None,
|
|
29
|
+
post_process=None,
|
|
30
|
+
**space_drawing_kwargs,
|
|
24
31
|
):
|
|
25
32
|
"""Create an Altair-based space visualization component.
|
|
26
33
|
|
|
27
34
|
Args:
|
|
28
35
|
agent_portrayal: Function to portray agents.
|
|
29
|
-
propertylayer_portrayal:
|
|
36
|
+
propertylayer_portrayal: Dictionary of PropertyLayer portrayal specifications
|
|
30
37
|
post_process :A user specified callable that will be called with the Chart instance from Altair. Allows for fine tuning plots (e.g., control ticks)
|
|
31
38
|
space_drawing_kwargs : not yet implemented
|
|
32
39
|
|
|
@@ -43,14 +50,23 @@ def make_altair_space(
|
|
|
43
50
|
return {"id": a.unique_id}
|
|
44
51
|
|
|
45
52
|
def MakeSpaceAltair(model):
|
|
46
|
-
return SpaceAltair(
|
|
53
|
+
return SpaceAltair(
|
|
54
|
+
model,
|
|
55
|
+
agent_portrayal,
|
|
56
|
+
propertylayer_portrayal=propertylayer_portrayal,
|
|
57
|
+
post_process=post_process,
|
|
58
|
+
)
|
|
47
59
|
|
|
48
60
|
return MakeSpaceAltair
|
|
49
61
|
|
|
50
62
|
|
|
51
63
|
@solara.component
|
|
52
64
|
def SpaceAltair(
|
|
53
|
-
model,
|
|
65
|
+
model,
|
|
66
|
+
agent_portrayal,
|
|
67
|
+
propertylayer_portrayal=None,
|
|
68
|
+
dependencies: list[any] | None = None,
|
|
69
|
+
post_process=None,
|
|
54
70
|
):
|
|
55
71
|
"""Create an Altair-based space visualization component.
|
|
56
72
|
|
|
@@ -63,10 +79,11 @@ def SpaceAltair(
|
|
|
63
79
|
# Sometimes the space is defined as model.space instead of model.grid
|
|
64
80
|
space = model.space
|
|
65
81
|
|
|
66
|
-
chart = _draw_grid(space, agent_portrayal)
|
|
82
|
+
chart = _draw_grid(space, agent_portrayal, propertylayer_portrayal)
|
|
67
83
|
# Apply post-processing if provided
|
|
68
84
|
if post_process is not None:
|
|
69
85
|
chart = post_process(chart)
|
|
86
|
+
|
|
70
87
|
solara.FigureAltair(chart)
|
|
71
88
|
|
|
72
89
|
|
|
@@ -138,7 +155,7 @@ def _get_agent_data_continuous_space(space: ContinuousSpace, agent_portrayal):
|
|
|
138
155
|
return all_agent_data
|
|
139
156
|
|
|
140
157
|
|
|
141
|
-
def _draw_grid(space, agent_portrayal):
|
|
158
|
+
def _draw_grid(space, agent_portrayal, propertylayer_portrayal):
|
|
142
159
|
match space:
|
|
143
160
|
case Grid():
|
|
144
161
|
all_agent_data = _get_agent_data_new_discrete_space(space, agent_portrayal)
|
|
@@ -168,23 +185,266 @@ def _draw_grid(space, agent_portrayal):
|
|
|
168
185
|
}
|
|
169
186
|
has_color = "color" in all_agent_data[0]
|
|
170
187
|
if has_color:
|
|
171
|
-
|
|
188
|
+
unique_colors = list({agent["color"] for agent in all_agent_data})
|
|
189
|
+
encoding_dict["color"] = alt.Color(
|
|
190
|
+
"color:N",
|
|
191
|
+
scale=alt.Scale(domain=unique_colors, range=unique_colors),
|
|
192
|
+
)
|
|
172
193
|
has_size = "size" in all_agent_data[0]
|
|
173
194
|
if has_size:
|
|
174
195
|
encoding_dict["size"] = alt.Size("size", type="quantitative")
|
|
175
196
|
|
|
176
|
-
|
|
197
|
+
agent_chart = (
|
|
177
198
|
alt.Chart(
|
|
178
199
|
alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict)
|
|
179
200
|
)
|
|
180
201
|
.mark_point(filled=True)
|
|
181
|
-
.properties(width=
|
|
182
|
-
# .configure_view(strokeOpacity=0) # hide grid/chart lines
|
|
202
|
+
.properties(width=300, height=300)
|
|
183
203
|
)
|
|
184
|
-
|
|
185
|
-
|
|
204
|
+
base_chart = None
|
|
205
|
+
cbar_chart = None
|
|
206
|
+
|
|
207
|
+
# This is the default value for the marker size, which auto-scales according to the grid area.
|
|
186
208
|
if not has_size:
|
|
187
209
|
length = min(space.width, space.height)
|
|
188
|
-
|
|
210
|
+
agent_chart = agent_chart.mark_point(size=30000 / length**2, filled=True)
|
|
211
|
+
|
|
212
|
+
if propertylayer_portrayal is not None:
|
|
213
|
+
chart_width = agent_chart.properties().width
|
|
214
|
+
chart_height = agent_chart.properties().height
|
|
215
|
+
base_chart, cbar_chart = chart_property_layers(
|
|
216
|
+
space=space,
|
|
217
|
+
propertylayer_portrayal=propertylayer_portrayal,
|
|
218
|
+
chart_width=chart_width,
|
|
219
|
+
chart_height=chart_height,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
base_chart = alt.layer(base_chart, agent_chart)
|
|
223
|
+
else:
|
|
224
|
+
base_chart = agent_chart
|
|
225
|
+
if cbar_chart is not None:
|
|
226
|
+
base_chart = alt.vconcat(base_chart, cbar_chart).configure_view(stroke=None)
|
|
227
|
+
return base_chart
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def chart_property_layers(space, propertylayer_portrayal, chart_width, chart_height):
|
|
231
|
+
"""Creates Property Layers in the Altair Components.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
space: the ContinuousSpace instance
|
|
235
|
+
propertylayer_portrayal:Dictionary of PropertyLayer portrayal specifications
|
|
236
|
+
chart_width: width of the agent chart to maintain consistency with the property charts
|
|
237
|
+
chart_height: height of the agent chart to maintain consistency with the property charts
|
|
238
|
+
agent_chart: the agent chart to layer with the property layers on the grid
|
|
239
|
+
Returns:
|
|
240
|
+
Altair Chart
|
|
241
|
+
"""
|
|
242
|
+
try:
|
|
243
|
+
# old style spaces
|
|
244
|
+
property_layers = space.properties
|
|
245
|
+
except AttributeError:
|
|
246
|
+
# new style spaces
|
|
247
|
+
property_layers = space._mesa_property_layers
|
|
248
|
+
base = None
|
|
249
|
+
bar_chart = None
|
|
250
|
+
for layer_name, portrayal in propertylayer_portrayal.items():
|
|
251
|
+
layer = property_layers.get(layer_name, None)
|
|
252
|
+
if not isinstance(
|
|
253
|
+
layer,
|
|
254
|
+
PropertyLayer | mesa.discrete_space.property_layer.PropertyLayer,
|
|
255
|
+
):
|
|
256
|
+
continue
|
|
189
257
|
|
|
190
|
-
|
|
258
|
+
data = layer.data.astype(float) if layer.data.dtype == bool else layer.data
|
|
259
|
+
|
|
260
|
+
if (space.width, space.height) != data.shape:
|
|
261
|
+
warnings.warn(
|
|
262
|
+
f"Layer {layer_name} dimensions ({data.shape}) do not match space dimensions ({space.width}, {space.height}).",
|
|
263
|
+
UserWarning,
|
|
264
|
+
stacklevel=2,
|
|
265
|
+
)
|
|
266
|
+
alpha = portrayal.get("alpha", 1)
|
|
267
|
+
vmin = portrayal.get("vmin", np.min(data))
|
|
268
|
+
vmax = portrayal.get("vmax", np.max(data))
|
|
269
|
+
colorbar = portrayal.get("colorbar", True)
|
|
270
|
+
|
|
271
|
+
# Prepare data for Altair (convert 2D array to a long-form DataFrame)
|
|
272
|
+
df = pd.DataFrame(
|
|
273
|
+
{
|
|
274
|
+
"x": np.repeat(np.arange(data.shape[0]), data.shape[1]),
|
|
275
|
+
"y": np.tile(np.arange(data.shape[1]), data.shape[0]),
|
|
276
|
+
"value": data.flatten(),
|
|
277
|
+
}
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if "color" in portrayal:
|
|
281
|
+
# Create a function to map values to RGBA colors with proper opacity scaling
|
|
282
|
+
def apply_rgba(val, vmin=vmin, vmax=vmax, alpha=alpha, portrayal=portrayal):
|
|
283
|
+
"""Maps data values to RGBA colors with opacity based on value magnitude.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
val: The data value to convert
|
|
287
|
+
vmin: The smallest value for which the color is displayed in the colorbar
|
|
288
|
+
vmax: The largest value for which the color is displayed in the colorbar
|
|
289
|
+
alpha: The opacity of the color
|
|
290
|
+
portrayal: The specifics of the current property layer in the iterative loop
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
String representation of RGBA color
|
|
294
|
+
"""
|
|
295
|
+
# Normalize value to range [0,1] and clamp
|
|
296
|
+
normalized = max(0, min((val - vmin) / (vmax - vmin), 1))
|
|
297
|
+
|
|
298
|
+
# Scale opacity by alpha parameter
|
|
299
|
+
opacity = normalized * alpha
|
|
300
|
+
|
|
301
|
+
# Convert color to RGB components
|
|
302
|
+
rgb_color = to_rgb(portrayal["color"])
|
|
303
|
+
r = int(rgb_color[0] * 255)
|
|
304
|
+
g = int(rgb_color[1] * 255)
|
|
305
|
+
b = int(rgb_color[2] * 255)
|
|
306
|
+
|
|
307
|
+
return f"rgba({r}, {g}, {b}, {opacity:.2f})"
|
|
308
|
+
|
|
309
|
+
# Apply color mapping to each value in the dataset
|
|
310
|
+
df["color"] = df["value"].apply(apply_rgba)
|
|
311
|
+
|
|
312
|
+
# Create chart for the property layer
|
|
313
|
+
chart = (
|
|
314
|
+
alt.Chart(df)
|
|
315
|
+
.mark_rect()
|
|
316
|
+
.encode(
|
|
317
|
+
x=alt.X("x:O", axis=None),
|
|
318
|
+
y=alt.Y("y:O", axis=None),
|
|
319
|
+
fill=alt.Fill("color:N", scale=None),
|
|
320
|
+
)
|
|
321
|
+
.properties(width=chart_width, height=chart_height, title=layer_name)
|
|
322
|
+
)
|
|
323
|
+
base = alt.layer(chart, base) if base is not None else chart
|
|
324
|
+
|
|
325
|
+
# Add colorbar if specified in portrayal
|
|
326
|
+
if colorbar:
|
|
327
|
+
# Extract RGB components from base color
|
|
328
|
+
rgb_color = to_rgb(portrayal["color"])
|
|
329
|
+
r_int = int(rgb_color[0] * 255)
|
|
330
|
+
g_int = int(rgb_color[1] * 255)
|
|
331
|
+
b_int = int(rgb_color[2] * 255)
|
|
332
|
+
|
|
333
|
+
# Define gradient endpoints
|
|
334
|
+
min_color = f"rgba({r_int},{g_int},{b_int},0)"
|
|
335
|
+
max_color = f"rgba({r_int},{g_int},{b_int},{alpha:.2f})"
|
|
336
|
+
|
|
337
|
+
# Define colorbar dimensions
|
|
338
|
+
colorbar_height = 20
|
|
339
|
+
colorbar_width = chart_width
|
|
340
|
+
|
|
341
|
+
# Create dataframe for gradient visualization
|
|
342
|
+
df_gradient = pd.DataFrame({"x": [0, 1], "y": [0, 1]})
|
|
343
|
+
|
|
344
|
+
# Create evenly distributed tick values
|
|
345
|
+
axis_values = np.linspace(vmin, vmax, 11)
|
|
346
|
+
tick_positions = np.linspace(0, colorbar_width, 11)
|
|
347
|
+
|
|
348
|
+
# Prepare data for axis and labels
|
|
349
|
+
axis_data = pd.DataFrame({"value": axis_values, "x": tick_positions})
|
|
350
|
+
|
|
351
|
+
# Create colorbar with linear gradient
|
|
352
|
+
colorbar_chart = (
|
|
353
|
+
alt.Chart(df_gradient)
|
|
354
|
+
.mark_rect(
|
|
355
|
+
x=0,
|
|
356
|
+
y=0,
|
|
357
|
+
width=colorbar_width,
|
|
358
|
+
height=colorbar_height,
|
|
359
|
+
color=alt.Gradient(
|
|
360
|
+
gradient="linear",
|
|
361
|
+
stops=[
|
|
362
|
+
alt.GradientStop(color=min_color, offset=0),
|
|
363
|
+
alt.GradientStop(color=max_color, offset=1),
|
|
364
|
+
],
|
|
365
|
+
x1=0,
|
|
366
|
+
x2=1, # Horizontal gradient
|
|
367
|
+
y1=0,
|
|
368
|
+
y2=0, # Keep y constant
|
|
369
|
+
),
|
|
370
|
+
)
|
|
371
|
+
.encode(
|
|
372
|
+
x=alt.value(chart_width / 2), # Center colorbar
|
|
373
|
+
y=alt.value(0),
|
|
374
|
+
)
|
|
375
|
+
.properties(width=colorbar_width, height=colorbar_height)
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Add tick marks to colorbar
|
|
379
|
+
axis_chart = (
|
|
380
|
+
alt.Chart(axis_data)
|
|
381
|
+
.mark_tick(thickness=2, size=8)
|
|
382
|
+
.encode(x=alt.X("x:Q", axis=None), y=alt.value(colorbar_height - 2))
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
# Add value labels below tick marks
|
|
386
|
+
text_labels = (
|
|
387
|
+
alt.Chart(axis_data)
|
|
388
|
+
.mark_text(baseline="top", fontSize=10, dy=0)
|
|
389
|
+
.encode(
|
|
390
|
+
x=alt.X("x:Q"),
|
|
391
|
+
text=alt.Text("value:Q", format=".1f"),
|
|
392
|
+
y=alt.value(colorbar_height + 10),
|
|
393
|
+
)
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Add title to colorbar
|
|
397
|
+
title = (
|
|
398
|
+
alt.Chart(pd.DataFrame([{"text": layer_name}]))
|
|
399
|
+
.mark_text(
|
|
400
|
+
fontSize=12,
|
|
401
|
+
fontWeight="bold",
|
|
402
|
+
baseline="bottom",
|
|
403
|
+
align="center",
|
|
404
|
+
)
|
|
405
|
+
.encode(
|
|
406
|
+
text="text:N",
|
|
407
|
+
x=alt.value(colorbar_width / 2),
|
|
408
|
+
y=alt.value(colorbar_height + 40),
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Combine all colorbar components
|
|
413
|
+
combined_colorbar = alt.layer(
|
|
414
|
+
colorbar_chart, axis_chart, text_labels, title
|
|
415
|
+
).properties(width=colorbar_width, height=colorbar_height + 50)
|
|
416
|
+
|
|
417
|
+
bar_chart = (
|
|
418
|
+
alt.vconcat(bar_chart, combined_colorbar)
|
|
419
|
+
.resolve_scale(color="independent")
|
|
420
|
+
.configure_view(stroke=None)
|
|
421
|
+
if bar_chart is not None
|
|
422
|
+
else combined_colorbar
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
elif "colormap" in portrayal:
|
|
426
|
+
cmap = portrayal.get("colormap", "viridis")
|
|
427
|
+
cmap_scale = alt.Scale(scheme=cmap, domain=[vmin, vmax])
|
|
428
|
+
|
|
429
|
+
chart = (
|
|
430
|
+
alt.Chart(df)
|
|
431
|
+
.mark_rect(opacity=alpha)
|
|
432
|
+
.encode(
|
|
433
|
+
x=alt.X("x:O", axis=None),
|
|
434
|
+
y=alt.Y("y:O", axis=None),
|
|
435
|
+
color=alt.Color(
|
|
436
|
+
"value:Q",
|
|
437
|
+
scale=cmap_scale,
|
|
438
|
+
title=layer_name,
|
|
439
|
+
legend=alt.Legend(title=layer_name) if colorbar else None,
|
|
440
|
+
),
|
|
441
|
+
)
|
|
442
|
+
.properties(width=chart_width, height=chart_height)
|
|
443
|
+
)
|
|
444
|
+
base = alt.layer(chart, base) if base is not None else chart
|
|
445
|
+
|
|
446
|
+
else:
|
|
447
|
+
raise ValueError(
|
|
448
|
+
f"PropertyLayer {layer_name} portrayal must include 'color' or 'colormap'."
|
|
449
|
+
)
|
|
450
|
+
return base, bar_chart
|
|
@@ -24,7 +24,7 @@ from matplotlib.colors import LinearSegmentedColormap, Normalize, to_rgba
|
|
|
24
24
|
from matplotlib.patches import Polygon
|
|
25
25
|
|
|
26
26
|
import mesa
|
|
27
|
-
from mesa.
|
|
27
|
+
from mesa.discrete_space import (
|
|
28
28
|
OrthogonalMooreGrid,
|
|
29
29
|
OrthogonalVonNeumannGrid,
|
|
30
30
|
VoronoiGrid,
|
|
@@ -40,8 +40,8 @@ from mesa.space import (
|
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
OrthogonalGrid = SingleGrid | MultiGrid | OrthogonalMooreGrid | OrthogonalVonNeumannGrid
|
|
43
|
-
HexGrid = HexSingleGrid | HexMultiGrid | mesa.
|
|
44
|
-
Network = NetworkGrid | mesa.
|
|
43
|
+
HexGrid = HexSingleGrid | HexMultiGrid | mesa.discrete_space.HexGrid
|
|
44
|
+
Network = NetworkGrid | mesa.discrete_space.Network
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def collect_agent_data(
|
|
@@ -101,7 +101,15 @@ def collect_agent_data(
|
|
|
101
101
|
stacklevel=2,
|
|
102
102
|
)
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
data = {
|
|
105
|
+
k: (np.asarray(v, dtype=object) if k == "marker" else np.asarray(v))
|
|
106
|
+
for k, v in arguments.items()
|
|
107
|
+
}
|
|
108
|
+
# ensures that the tuples in marker dont get converted by numpy to an array resulting in a 2D array
|
|
109
|
+
arr = np.empty(len(arguments["marker"]), dtype=object)
|
|
110
|
+
arr[:] = arguments["marker"]
|
|
111
|
+
data["marker"] = arr
|
|
112
|
+
return data
|
|
105
113
|
|
|
106
114
|
|
|
107
115
|
def draw_space(
|
|
@@ -133,7 +141,7 @@ def draw_space(
|
|
|
133
141
|
# https://stackoverflow.com/questions/67524641/convert-multiple-isinstance-checks-to-structural-pattern-matching
|
|
134
142
|
match space:
|
|
135
143
|
# order matters here given the class structure of old-style grid spaces
|
|
136
|
-
case HexSingleGrid() | HexMultiGrid() | mesa.
|
|
144
|
+
case HexSingleGrid() | HexMultiGrid() | mesa.discrete_space.HexGrid():
|
|
137
145
|
draw_hex_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs)
|
|
138
146
|
case (
|
|
139
147
|
mesa.space.SingleGrid()
|
|
@@ -142,7 +150,7 @@ def draw_space(
|
|
|
142
150
|
| mesa.space.MultiGrid()
|
|
143
151
|
):
|
|
144
152
|
draw_orthogonal_grid(space, agent_portrayal, ax=ax, **space_drawing_kwargs)
|
|
145
|
-
case mesa.space.NetworkGrid() | mesa.
|
|
153
|
+
case mesa.space.NetworkGrid() | mesa.discrete_space.Network():
|
|
146
154
|
draw_network(space, agent_portrayal, ax=ax, **space_drawing_kwargs)
|
|
147
155
|
case (
|
|
148
156
|
mesa.space.ContinuousSpace()
|
|
@@ -221,7 +229,7 @@ def draw_property_layers(
|
|
|
221
229
|
layer = property_layers.get(layer_name, None)
|
|
222
230
|
if not isinstance(
|
|
223
231
|
layer,
|
|
224
|
-
PropertyLayer | mesa.
|
|
232
|
+
PropertyLayer | mesa.discrete_space.property_layer.PropertyLayer,
|
|
225
233
|
):
|
|
226
234
|
continue
|
|
227
235
|
|
|
@@ -638,8 +646,8 @@ def _scatter(ax: Axes, arguments, **kwargs):
|
|
|
638
646
|
f"{entry} is specified in agent portrayal and via plotting kwargs, you can only use one or the other"
|
|
639
647
|
)
|
|
640
648
|
|
|
641
|
-
for mark in
|
|
642
|
-
mark_mask =
|
|
649
|
+
for mark in set(marker):
|
|
650
|
+
mark_mask = [m == mark for m in list(marker)]
|
|
643
651
|
for z_order in np.unique(zorder):
|
|
644
652
|
zorder_mask = z_order == zorder
|
|
645
653
|
logical = mark_mask & zorder_mask
|