Skip to content

Commit 81f83a2

Browse files
authored
Merge pull request #1 from danylaksono/plugins
Plugins
2 parents 19ad462 + fb7986c commit 81f83a2

File tree

10 files changed

+2301
-16
lines changed

10 files changed

+2301
-16
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
A GPU/Canvas hybrid Screen-Space Grid Aggregation library for MapLibre GL JS. This library provides efficient real-time aggregation of point data into screen-space grids with customizable styling, interactive features, and advanced glyph drawing capabilities.
66

7+
This library is inspired by Aidan Slingsby's Gridded Glyphmaps and deck.gl's `ScreenGridLayer` but is built from the ground up with a modular architecture, focusing on performance, flexibility, and ease of use.
8+
79
![](./screengrid.png)
810

911
## 🚀 Features
@@ -398,7 +400,7 @@ MIT License - see LICENSE file for details.
398400
4. Consider TypeScript definitions
399401

400402
### Long-term
401-
1. Plugin system for custom glyphs
403+
1. Plugin system for custom glyphs
402404
2. WebGL renderer alternative
403405
3. Server-side aggregation
404406
4. Framework bindings (React, Vue)

docs/PLUGIN_GLYPHS.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Plugin Glyphs — Design & API
2+
3+
This document describes the design, API, and usage for the ScreenGrid glyph plugin system.
4+
5+
📖 **📚 For comprehensive documentation**: See [PLUGIN_GLYPH_ECOSYSTEM.md](./PLUGIN_GLYPH_ECOSYSTEM.md) for detailed evaluation, all available plugins, usage patterns, and complete API reference.
6+
7+
Status: ✅ **Fully implemented and production-ready**. Complete implementation in `src/glyphs/GlyphRegistry.js` with full integration into `ScreenGridLayerGL.js`. Four built-in plugins available, lifecycle hooks implemented, and legend support functional.
8+
9+
## Goals
10+
11+
- Provide a lightweight, JS-first plugin registry so users and third-party packages can register reusable glyphs by name.
12+
- Keep the existing `onDrawCell` callback fully supported and highest precedence for backward compatibility.
13+
- Offer built-in glyphs registered under friendly names (e.g. `circle`, `bar`, `pie`, `heatmap`).
14+
- Keep the runtime path fast and safe: plugins run synchronously on the main thread and must be reasonably fast.
15+
16+
## High-level contract
17+
18+
- Registration: `GlyphRegistry.register(name, plugin, { overwrite=false })`
19+
- Plugin shape (required/optional fields):
20+
- `draw(ctx, x, y, normalizedValue, cellInfo, config)` — required. Draw into the provided Canvas 2D context.
21+
- `init?(opts)` — optional. Called by a layer if the layer wants to invoke initialization. (Not used in MVP.)
22+
- `destroy?()` — optional. Called on layer removal if used.
23+
- `getLegend?(cellInfo)` — optional. Return legend entries for the cell.
24+
25+
## Layer integration
26+
27+
New/updated `ScreenGridLayerGL` options (MVP):
28+
29+
- `glyph` (string | null): Registered glyph name to use. If provided and a plugin exists with that name, the plugin's `draw` will be invoked for each cell.
30+
- `glyphConfig` (object): Arbitrary object passed as the last argument to `plugin.draw` and available for built-in wrappers.
31+
- Precedence order when rendering glyphs (highest -> lowest):
32+
1. `onDrawCell` callback from the layer's config (unchanged behavior)
33+
2. `glyph` name resolved from `GlyphRegistry`
34+
3. Color-mode rendering (no glyphs)
35+
36+
Implementation detail: the layer constructs a wrapper `onDrawCell` function from the plugin and passes that to the existing `Renderer` pipeline. No changes to `Renderer` were required.
37+
38+
## Example usage
39+
40+
Register a glyph in your app startup code:
41+
42+
```javascript
43+
import { GlyphRegistry } from 'screengrid/src/glyphs/GlyphRegistry.js';
44+
45+
GlyphRegistry.register('myGauge', {
46+
draw(ctx, x, y, normalizedValue, cellInfo, config) {
47+
const radius = config && config.radius ? config.radius : cellInfo.glyphRadius;
48+
// Draw a simple gauge using canvas
49+
ctx.fillStyle = config.color || 'steelblue';
50+
ctx.beginPath();
51+
ctx.arc(x, y, radius, 0, 2 * Math.PI);
52+
ctx.fill();
53+
// Add more drawing based on normalizedValue or cellInfo
54+
}
55+
});
56+
57+
// Then create a layer using the registered glyph
58+
const layer = new ScreenGridLayerGL({
59+
data,
60+
glyph: 'myGauge',
61+
glyphConfig: { radius: 8, color: '#ff6600' },
62+
enableGlyphs: true,
63+
});
64+
```
65+
66+
Notes:
67+
68+
- You can still provide `onDrawCell` directly (anonymous function or closure); it takes precedence over `glyph`.
69+
- Built-in glyphs are available by default: `circle`, `bar`, `pie`, `heatmap`.
70+
71+
## Error handling and performance guidance
72+
73+
- The layer wraps plugin execution in a try/catch and logs errors. A faulty plugin will not break the whole render loop.
74+
- Plugins run synchronously during render. For heavy computations, precompute during aggregation (`onAggregate`) and store summarised data on `cellInfo` so `draw` remains fast.
75+
- Avoid network fetches or long-running sync work inside `draw`.
76+
77+
## Backwards compatibility & migration
78+
79+
- No change required for existing users using `onDrawCell`.
80+
- To use a registered glyph instead of a callback, set `glyph: '<name>'` and optionally `glyphConfig` on the layer.
81+
82+
## Future improvements (post-MVP)
83+
84+
-**Implemented**: Per-layer plugin lifecycle hooks (`plugin.init(layer, config)` and `plugin.destroy()`)
85+
-**Implemented**: Legend integration (Legend module calls `plugin.getLegend()` when available)
86+
-**Planned**: Optional sandboxing: consider WebWorker or sandboxed if third-party plugins run untrusted code.
87+
-**Planned**: Dynamic loading: allow registering plugins from URLs (with security considerations).
88+
89+
## Files changed in this PR
90+
91+
- `src/glyphs/GlyphRegistry.js` — new registry and built-in adapters
92+
- `src/ScreenGridLayerGL.js` — wired the registry into the draw pipeline (wraps plugin.draw into onDrawCell)
93+
- `docs/PLUGIN_GLYPHS.md` — this design doc
94+
95+
## Minimal testing checklist
96+
97+
1. Register a simple plugin and confirm it's called when `glyph` is set.
98+
2. Confirm `onDrawCell` still takes precedence.
99+
3. Confirm built-in names ('circle', 'bar', 'pie', 'heatmap') draw something.
100+
4. Confirm errors thrown in plugin.draw are logged and do not halt rendering.

0 commit comments

Comments
 (0)