Front end: Solara icons integration
Summary
Adds opt-in icon (image) rendering to Mesa's Altair-based visualization, allowing agents to be displayed as SVG icons instead of simple markers.
Changes
Core Implementation
icon_cache.py: Caches rasterized icons as base64 data URLs to avoid per-frame conversion overheadicons.py: Utility functions for loading bundled and custom iconsicons/*.svg: Three bundled icons (smiley, sad_face, neutral_face) usingcurrentColorfor dynamic coloringaltair_backend.py: Addedmark_imagelayer support for icon rendering alongside existingmark_point
Demo
solara_viz_icon.py: Interactive demo showing grid-based icon visualization with Solara
Tests
- Added 11 unit tests covering icon path resolution and cache functionality
How It Works
- Agent portrayals can specify
"icon": "smiley"(or path to custom SVG/PNG) - Icons are rasterized once via
cairosvgand cached as base64 data URLs - Altair renders icons using
mark_imagelayer on top of the base chart
Usage Example
def agent_portrayal(agent):
return {
"x": agent.pos[0],
"y": agent.pos[1],
"icon": "smiley", # Use bundled icon
"color": "#FFD700",
"size": 32,
}
Performance
Icon rendering adds ~1% overhead compared to marker-only rendering (see benchmarks in #2902).
Dependencies :
cairosvg for SVG→PNG rasterization (optional, only needed if using icons)
Related
--> Follows up on benchmarks from #2902
--> Addresses request for image-based agent visualization
Performance benchmarks:
| Model | Size | Init time [95% CI] | Run time [95% CI] |
|---|---|---|---|
| BoltzmannWealth | small | 🔵 -0.5% [-1.3%, +0.2%] | 🔵 -2.4% [-2.7%, -2.2%] |
| BoltzmannWealth | large | 🔵 +0.1% [-17.0%, +20.4%] | 🔵 -0.7% [-2.9%, +1.4%] |
| Schelling | small | 🔵 +0.7% [+0.6%, +0.9%] | 🔵 -0.1% [-0.3%, +0.0%] |
| Schelling | large | 🔵 +5.2% [-10.6%, +24.4%] | 🔵 +10.7% [-4.7%, +33.9%] |
| WolfSheep | small | 🔵 +0.1% [-0.1%, +0.3%] | 🔵 -1.9% [-2.1%, -1.8%] |
| WolfSheep | large | 🔵 +3.7% [-30.1%, +49.0%] | 🔵 +16.7% [-13.2%, +50.4%] |
| BoidFlockers | small | 🔵 +1.4% [+1.0%, +1.8%] | 🔵 +0.6% [+0.4%, +0.8%] |
| BoidFlockers | large | 🔵 +1.3% [+1.0%, +1.6%] | 🔵 +0.4% [+0.3%, +0.6%] |
Thanks for this effort!
At over 2000 lines of code this is an insanely large PR. That also makes it difficult to review.
- Is this PR in it's finished state? Or more a proof of concept?
- How large would a minimum implementation be?
- What's the quickest way for a reviewer to validate/reproduce the desired behavior?
- Have you investigated the test failures?
Hello,
--> This PR is a finished implementation, not just a proof of concept. The large diff is mainly due to the Solara demo and documentation, the core backend changes are much smaller. --> A minimal implementation (core icon rendering in Altair) would be under 300 lines, excluding the demo and docs. --> To quickly validate: run [solara run mesa/visualization/solara_viz_icon.py]( for the interactive demo, or use the new [icon] key in agent portrayals with the Altair backend. --> All tests pass except for one (test_call_space_drawer in [test_solara_viz.py]. I will investigate and resolve this issue. All other tests are passing. Let me know if you’d prefer a split PR (core backend vs. demo/docs). What do you think?
Thanks for the quick and detailed reply.
Let me know if you’d prefer a split PR
Yes please. Target a "production ready" PR, so production code + essential documentation.
What we often do when integrating a new feature, is updating one example model in mesa/examples. That makes sure the feature is directly tested and documented though code.
I did pip install cairosvg (on Windows), but I need also a non-pip dependency?
OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found
cannot load library 'libcairo.so.2': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.so.2'
cannot load library 'libcairo.2.dylib': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo.2.dylib'
cannot load library 'libcairo-2.dll': error 0x7e. Additionally, ctypes.util.find_library() did not manage to locate a library called 'libcairo-2.dll'
Adding a new dependency (cairosvg) is not ideal but might be proportional, but I don't think asking users to install anything not-pip is.
Thank you for the feedback . I’ll split the PR as suggested: one for production code and essential documentation only. I’ll also update one example model in [mesa/examples] to demonstrate and test the new feature.
Regarding the cairosvg dependency: I agree that requiring users to install non-pip system libraries (like libcairo) is not ideal. I’ll investigate alternatives to avoid this requirement.