mesa icon indicating copy to clipboard operation
mesa copied to clipboard

Front end: Solara icons integration

Open Mani212005 opened this issue 2 weeks ago • 6 comments

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.

image

Changes

Core Implementation

  • icon_cache.py: Caches rasterized icons as base64 data URLs to avoid per-frame conversion overhead
  • icons.py: Utility functions for loading bundled and custom icons
  • icons/*.svg: Three bundled icons (smiley, sad_face, neutral_face) using currentColor for dynamic coloring
  • altair_backend.py: Added mark_image layer support for icon rendering alongside existing mark_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

  1. Agent portrayals can specify "icon": "smiley" (or path to custom SVG/PNG)
  2. Icons are rasterized once via cairosvg and cached as base64 data URLs
  3. Altair renders icons using mark_image layer 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

Mani212005 avatar Dec 07 '25 19:12 Mani212005

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%]

github-actions[bot] avatar Dec 07 '25 20:12 github-actions[bot]

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?

EwoutH avatar Dec 08 '25 06:12 EwoutH

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?

Mani212005 avatar Dec 08 '25 08:12 Mani212005

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.

EwoutH avatar Dec 08 '25 08:12 EwoutH

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.

EwoutH avatar Dec 08 '25 08:12 EwoutH

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.

Mani212005 avatar Dec 08 '25 09:12 Mani212005