feat(render): rewrite VL→matplotlib as a staged tree-walking interpreter - #1
Merged
Merged
Conversation
Replace the ad-hoc per-mark render.py prototype with a small interpreter package (molplot.vlmpl) structured as four compiler passes: normalize → bind scales → dispatch marks → finalize axes. Mark encoders read typed channels instead of hardcoded field names, so marks and scales are extensible. Fixes two Vega-Lite → matplotlib parity gaps the prototype had: - positional scales are applied: log axes (scale.type) and explicit domains (scale.domain → axis limits) now reach the figure. - per-layer transform filters are honoured, so a plain line chart no longer draws the stray marker-layer points; the detail channel splits series that share a legend colour. render.py becomes a thin shim re-exporting molplot.vlmpl.render, preserving the public molplot.render API. Adds test_vlmpl.py for the fixed behaviour.
Roy-Kid
marked this pull request as ready for review
July 4, 2026 07:49
Roy-Kid
added a commit
that referenced
this pull request
Jul 10, 2026
…ter (#1) Replace the ad-hoc per-mark render.py prototype with a small interpreter package (molplot.vlmpl) structured as four compiler passes: normalize → bind scales → dispatch marks → finalize axes. Mark encoders read typed channels instead of hardcoded field names, so marks and scales are extensible. Fixes two Vega-Lite → matplotlib parity gaps the prototype had: - positional scales are applied: log axes (scale.type) and explicit domains (scale.domain → axis limits) now reach the figure. - per-layer transform filters are honoured, so a plain line chart no longer draws the stray marker-layer points; the detail channel splits series that share a legend colour. render.py becomes a thin shim re-exporting molplot.vlmpl.render, preserving the public molplot.render API. Adds test_vlmpl.py for the fixed behaviour.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Rewrites the Vega-Lite → matplotlib renderer from an ad-hoc,
specs.py-coupledprototype into a small tree-walking interpreter (
molplot.vlmpl) whosesource language is Vega-Lite and target machine is matplotlib.
Four passes, each a classic compiler stage:
model.py— normalize the raw spec into an immutable, field-name-agnosticAST (
Unit/Channel/Scale); resolveslayerinheritance and mark shape.scales.py— bind scales: colour maps, per-series numeric maps, andpositional axis config.
marks.py— dispatch each mark to an encoder (MARK_ENCODERSregistry);encoders read typed channels (
enc["x"].field), never hardcoded field names.axes.py— finalize titles, positional scales, and legend.render.pyis now a thin shim re-exportingmolplot.vlmpl.render; the publicmolplot.render(spec, *, preset, mode, ax, apply_style)API is unchanged.Why — two parity gaps the prototype had
specs.pyemitsscale.type: "log"andscale.domain, but the old renderer never applied them — a web log axisrendered linear in matplotlib, and
x_domain/y_domainwere ignored. Theaxespass now applies both.transformfilters were ignored. The line spec's marker layeris gated by
transform: [{filter: {oneOf: markerKeys}}]; the old rendererdrew marker points on every line chart. Filters are now honoured, and the
detailchannel splits series that share a legend colour.Tests
test_vlmpl.pyadds coverage for the fixed behaviour (log axis, domain limits,no stray markers, detail splitting). Full suite: 33 passed (27 existing + 6
new).