-
Notifications
You must be signed in to change notification settings - Fork 2
Coverage
Is the set of primitives this package builds the right set? The
Backlog's "Foundation review" answers yes, but argues it from
capability alone: canvas can fill any shape it can describe, so a
charting layer never has to drop to raw pixels. That's a real argument
and it's still true, but it's an internal one — it says the package can
draw anything, not that it draws the things visualizations actually
need.
This page checks the same claim from outside, against what real visualizations use and what an independently designed graphics grammar chose to ship. Both point the same way, which is the useful part: the conclusion doesn't rest on this project's own taste.
Beagle (Battle et al., CHI 2018) is the largest corpus study to hand. It scraped 41,000+ SVG visualizations from the web across five tools and repositories (D3, Plotly, ChartBlocks, FusionCharts, Graphiq) and classified them into 24 types at 86% accuracy.
The distribution is heavily unbalanced. Most visualizations fall under four types — bar, line, scatter, and geographic maps — with simple bar charts and histograms making up the largest share by a wide margin.
Two findings are worth keeping in mind because they cut against intuition:
- Pie charts are rare in practice. They occupy far more space in chart-type debates than in real corpora.
- The long tail is genuinely long but genuinely thin. 24 types were classifiable; four dominate. Breadth of chart vocabulary is not what most real work needs.
Vega-Lite settled on
a small, closed set of primitive marks: area, bar, circle, line,
point, rect, rule, square, text, tick, geoshape, plus
arc. Everything more complex is a composite mark — boxplot,
errorbar, errorband are macros expanding to layered primitives, not
new geometry.
That is a research group that studied this space arriving at roughly a dozen primitives and deriving the rest. It matches Wilkinson's original decomposition in The Grammar of Graphics and ggplot2's geom set, so the convergence isn't coincidental.
Every Vega-Lite primitive mark, against what this package already exports:
| Mark | canvas_mojo |
|---|---|
bar, rect, square
|
fill_rect |
circle |
fill_circle_aa |
| ellipse (not a Vega-Lite mark; error ellipses, confidence regions) |
fill_ellipse_aa, draw_ellipse_aa
|
line, rule, tick
|
draw_line_aa, draw_polyline_aa
|
area |
fill_path_aa |
arc (pie, donut) |
fill_arc_aa, fill_ring_sector_aa
|
text |
draw_text |
geoshape |
fill_path_aa |
boxplot, errorbar, errorband (composite) |
fill_rect + draw_line_aa
|
point with a non-circular shape |
fill_path_aa, no symbol vocabulary |
| surface (not a Vega-Lite mark; 3D surface and heightmap plots) |
fill_mesh, fill_mesh_shaded
|
The vocabulary is covered, and the two dominant corpus types — bar and line — land on the two cheapest primitives in the package.
Two gaps, different in kind — one still open by choice, one closed.
Shaped point markers (triangle, diamond, cross, star) have no
preset. This is deliberate and already recorded in the
Backlog; the external evidence only strengthens the existing
reasoning. Each marker is 3–6 Path calls with no new geometry behind
it, and a marker set is chart vocabulary — the layer that knows which
markers a scatter plot wants is the charting layer, not this one.
d3-shape makes the same split,
shipping seven symbols in a module above the rendering layer.
Ellipse was not reachable through DrawTarget — and unlike the
markers, this was a real gap rather than a deferral, which is the one
thing this review turned up that the internal capability argument had
missed. fill_ellipse_aa/draw_ellipse_aa existed as raster
primitives, but neither was on the trait and SvgCanvas had no ellipse
method at all. Normally "use fill_path_aa" covers a missing shape —
but Path.arc_to takes a single radius, so it builds circular arcs
only, and an ellipse can therefore only be approximated through
Path, with cubics.
Both halves were added to the trait as a result (see the Changelog):
fill_ellipse_aa, then draw_ellipse_aa once it was clear the outline
hits the identical limitation. That makes the ellipse the only shape on
the trait carrying a fill and an outline, where circles and arcs carry
only a fill — an asymmetry that is exactly right, since a circle outline
is stroke_path_aa over a one-arc_to Path and an ellipse outline
has no equivalent.
The general lesson is worth more than the fix: "you can always build it
as a Path" is the standing justification for keeping the trait
narrow, and it holds for every shape except the ones needing a curve
Path can't express exactly. Circular arcs it has; elliptical arcs it
does not. Anything else in that category would be a real gap too.
Because shape is a weak encoding channel, and adding shapes has sharply diminishing returns.
Shape has no numerical ordering — it is selective but not quantitative, in Bertin's terms — so there is no clean capacity number the way there is for position or length. Shape It Up (Tseng et al., TVCG 2025) evaluated 39 shapes across three tasks and found that performance "does not map well to classical features of shape such as angles, fill, or convex hull" — the intuitive ways of reasoning about which shapes pair well simply don't predict how they perform. Their recommendations come from measured pairwise relations between shapes instead. So a library cannot pick a good shape set by reasoning from shape properties; that has to come from perceptual data.
The binding constraint in practice is also not availability but discriminability under overlap: shapes have to be relatively large to tell apart, which worsens occlusion exactly in the dense scatterplots where a categorical encoding is most wanted (van Onzenoodt et al., 2020). Existing libraries bracket the reasonable range — d3 ships 7 symbols, ggplot2 ships 25 — and the corpus evidence above argues for spending effort on bar/line/scatter quality over shape breadth.
Worth stating plainly, so this page isn't over-read:
-
Corpus frequency is not your requirement.
fill_arc_aaandfill_ring_sector_aaare on the trait despite pie charts being rare in Beagle's corpus, because a real downstream donut chart needed them. A concrete caller beats a population statistic every time; the corpus tells you where the mass is, not what you're building. - Beagle sampled the web, and SVG specifically. Scientific plotting, print, and canvas-rendered visualizations are underrepresented. A library aimed at those would weigh error bars, contours, and small-multiple layouts more heavily than this distribution suggests.
- Coverage is not quality. That every mark maps to a primitive says nothing about whether the output looks right at chart sizes. That question is answered by the antialiasing and text work, not by this page.
- Battle et al., Beagle: Automated Extraction and Interpretation of Visualizations from the Web, CHI 2018 — PDF
- Vega-Lite mark documentation
- Tseng, Wang, Quadri & Szafir, Shape It Up: An Empirically Grounded Approach for Designing Shape Palettes, IEEE TVCG 2025, 31(1)
- van Onzenoodt, Huckauf & Ropinski, On the perceptual influence of shape overlap on data-comparison using scatterplots, Computers & Graphics 90 (2020), 169–181
- d3-shape symbols