Skip to content

Commit 55770cd

Browse files
Added AgentPortrayalStyle and PropertyLayerStyle (#2786)
* added AgentPortrayalStyle and PropertyLayerStyle * added docstrings * added coderabbit review * added usage example and fix __init__ file * added backwards compatability for dict propertylayer_portrayal * fix the network logic * null check for agent pos * added tests * delete unwanted test
1 parent aaee315 commit 55770cd

File tree

6 files changed

+445
-169
lines changed

6 files changed

+445
-169
lines changed

mesa/examples/advanced/sugarscape_g1mt/app.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
22
from mesa.visualization import Slider, SolaraViz, make_plot_component
3+
from mesa.visualization.components import AgentPortrayalStyle, PropertyLayerStyle
34
from mesa.visualization.components.matplotlib_components import make_mpl_space_component
45

56

67
def agent_portrayal(agent):
7-
return {"marker": "o", "color": "red", "size": 10}
8+
return AgentPortrayalStyle(
9+
x=agent.cell.coordinate[0],
10+
y=agent.cell.coordinate[1],
11+
color="red",
12+
marker="o",
13+
size=10,
14+
zorder=1,
15+
)
816

917

10-
propertylayer_portrayal = {
11-
"sugar": {"color": "blue", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
12-
"spice": {"color": "red", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
13-
}
18+
def propertylayer_portrayal(layer):
19+
if layer.name == "sugar":
20+
return PropertyLayerStyle(
21+
color="blue", alpha=0.8, colorbar=True, vmin=0, vmax=10
22+
)
23+
return PropertyLayerStyle(color="red", alpha=0.8, colorbar=True, vmin=0, vmax=10)
1424

1525

1626
sugarscape_space = make_mpl_space_component(

mesa/examples/advanced/sugarscape_g1mt/tests.py

Lines changed: 0 additions & 69 deletions
This file was deleted.

mesa/visualization/components/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""custom solara components."""
1+
"""Custom visualization components."""
22

33
from __future__ import annotations
44

@@ -10,6 +10,19 @@
1010
make_mpl_plot_component,
1111
make_mpl_space_component,
1212
)
13+
from .portrayal_components import AgentPortrayalStyle, PropertyLayerStyle
14+
15+
__all__ = [
16+
"AgentPortrayalStyle",
17+
"PropertyLayerStyle",
18+
"SpaceAltair",
19+
"SpaceMatplotlib",
20+
"make_altair_space",
21+
"make_mpl_plot_component",
22+
"make_mpl_space_component",
23+
"make_plot_component",
24+
"make_space_component",
25+
]
1326

1427

1528
def make_space_component(
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Portrayal Components Module.
2+
3+
This module defines data structures for styling visual elements in Mesa agent-based model visualizations.
4+
It provides user-facing classes to specify how agents and property layers should appear in the rendered space.
5+
6+
Classes:
7+
- AgentPortrayalStyle: Controls the appearance of individual agents (e.g., color, shape, size, etc.).
8+
- PropertyLayerStyle: Controls the appearance of background property layers (e.g., color gradients or uniform fills).
9+
10+
These components are designed to be passed into Mesa visualizations to customize and standardize how data is presented.
11+
"""
12+
13+
from dataclasses import dataclass
14+
from typing import Any
15+
16+
17+
@dataclass
18+
class AgentPortrayalStyle:
19+
"""Represents the visual styling options for an agent in a visualization.
20+
21+
User facing component to control how agents are drawn.
22+
Allows specifying properties like color, size,
23+
marker shape, position, and other plot attributes.
24+
"""
25+
26+
x: float | None = None
27+
y: float | None = None
28+
color: str | tuple | None = "tab:blue"
29+
marker: str | None = "o"
30+
size: int | float | None = 50
31+
zorder: int | None = 1
32+
alpha: float | None = 1.0
33+
edgecolors: str | tuple | None = None
34+
linewidths: float | int | None = 1.0
35+
36+
def update(self, *updates_fields: tuple[str, Any]):
37+
"""Updates attributes from variable (field_name, new_value) tuple arguments.
38+
39+
Example:
40+
>>> def agent_portrayal(agent):
41+
>>> primary_style = AgentPortrayalStyle(color="blue", marker="^", size=10, x=agent.pos[0], y=agent.pos[1])
42+
>>> if agent.type == 1:
43+
>>> primary_style.update(("color", "red"), ("size", 30))
44+
>>> return primary_style
45+
"""
46+
for field_to_change, field_to_change_to in updates_fields:
47+
if hasattr(self, field_to_change):
48+
setattr(self, field_to_change, field_to_change_to)
49+
else:
50+
raise AttributeError(
51+
f"'{type(self).__name__}' object has no attribute '{field_to_change}'"
52+
)
53+
54+
55+
@dataclass
56+
class PropertyLayerStyle:
57+
"""Represents the visual styling options for a property layer in a visualization.
58+
59+
User facing component to control how property layers are drawn.
60+
Allows specifying properties like colormap, single color, value limits,
61+
and colorbar visibility.
62+
63+
Note: You can specify either a 'colormap' (for varying data) or a single
64+
'color' (for a uniform layer appearance), but not both simultaneously.
65+
"""
66+
67+
colormap: str | None = None
68+
color: str | None = None
69+
alpha: float = 0.8
70+
colorbar: bool = True
71+
vmin: float | None = None
72+
vmax: float | None = None
73+
74+
def __post_init__(self):
75+
"""Validate that color and colormap are not simultaneously specified."""
76+
if self.color is not None and self.colormap is not None:
77+
raise ValueError("Specify either 'color' or 'colormap', not both.")
78+
if self.color is None and self.colormap is None:
79+
raise ValueError("Specify one of 'color' or 'colormap'")

0 commit comments

Comments
 (0)