目录

moon_vegalite

A spec-driven Vega-Lite renderer written in MoonBit. A Vega-Lite JSON specification goes in; a backend-neutral scenegraph and an SVG string come out. It runs on the wasm-gc, js and native backends and has no dependency outside the MoonBit core library.

let svg = @moon_vegalite.render_svg(spec_json)        // SVG document
let c = @moon_vegalite.compile(spec_json)             // scenegraph + view metadata
let scene = @moon_vegalite.scene_json(c)              // the scenegraph as JSON
moon build --target native cmd/vl2svg
vl2svg spec.vl.json chart.svg        # SVG file (stdout without the second argument)
vl2svg --scene spec.vl.json          # scenegraph JSON, with the bounds of every item

Repositories: GitHub · GitLink.

See the gallery: the SVG from this library next to the official vega output.

Positioning and differences from other charting libraries

  • It follows Vega-Lite, not a new grammar. The goal is the same picture the reference implementation draws (vega-lite 5.23.0 + vega 5.33.1), not a similar one: axis ticks, label formats, legends, layout and mark bounds are ported from vega-lite / vega, including their JavaScript number and text-measurement quirks.
  • Scenegraph first. Like Vega, the library produces a tree of marks and items with computed bounds. SVG is one renderer of that tree; other backends can consume it. Most MoonBit charting code draws directly; here the intermediate form is public.
  • Specifications in, pictures out. Charts are data (JSON), so they can come from other tools. The test-suite feeds specs generated by MoonFrame (DataFrame → Vega-Lite) to the renderer.
  • No browser, no JavaScript runtime. Rendering is a pure function, usable in a native CLI, in WebAssembly, or in JS.
  • Honest about scope. It is a subset renderer. Anything outside the subset fails with an error that names the JSON path (encoding.color.condition 暂不支持) instead of drawing something different.

Support

Area Supported
Marks bar, line, point, circle, square, area, rule, tick, rect, text
Channels x, y, x2, y2, color (nominal/ordinal fields with the category scheme, quantitative fields with a blues ramp and gradient legend, constant values), size, opacity, shape, text (size, opacity and shape on point, circle, square; quantitative fields give continuous scales, shape is nominal)
Field types quantitative, ordinal, nominal, temporal
Aggregates count, sum, mean, average, min, max, median, distinct, … (vega aggregate ops)
Transforms filter (expression or predicate), calculate (expression subset), aggregate, bin, timeUnit, fold; field bin, timeUnit, aggregate
Time units year, quarter, month, week, date, day, hours, minutes, seconds, milliseconds and combinations (UTC; “local” time is UTC)
Scales linear, log, sqrt, pow, time, utc, band, point, ordinal (domain, range, zero, nice, reverse, clamp, base, exponent)
Stacking default stacking for bar and area; stack: zero / center / normalize / null
Sorting sort on discrete x / y: ascending, descending, a value list, -x / -y, {op, field, order}
Guides axes (grid, ticks, labels with flush / overlap removal, title), symbol legends, chart title
Layout single view, autosize: pad, width / height as numbers or {step}
Config accepted keys are recorded; a subset (view, scale.zero, …) is honoured

Not supported — each raises an error that names the JSON path: params / selection, tooltip, projection, layer, facet, concat (hconcat / vconcat), repeat, data.url (embed data.values instead), condition, and the full Vega expression language (only the subset used by calculate / filter is implemented). Gradient legends for quantitative colour is limited to the default blues ramp (no scale options on non-positional channels). Anything the compiler would otherwise ignore is rejected with the JSON path: unknown mark properties, config keys other than scale.zero, axis / scale / legend options that are not implemented, sort on other channels, and so on (vl/validate.mbt).

Accuracy against the official implementation

Every expected value in the tests comes from the official packages, never from this repository:

Layer Reference Where
number formatting, ticks, nice, time formats, scales d3 / vega-format / vega-scale as bundled in vega 5.33.1 fixtures/{numfmt,ticks,formatcases,timecases,scalecases}
expressions, data transforms vega-expression, vega-transforms fixtures/{exprcases,transformcases}
scenegraph bounds, SVG vega-scenegraph fixtures/scenecases
end to end: 60 official Vega-Lite examples, edge cases, MoonFrame specs vega-lite 5.23.0 compile + vega scenegraph and SVG fixtures/vlexamples

For the end-to-end cases the scenegraph (every mark, item property and bounds, numbers within a relative 1e-9) and the SVG text (exactly, ignoring only the accessibility role / aria-label attributes) must equal the official output. The cases cover the 60 official examples listed in examples/official.txt, the edge cases in examples/edge_*.vl.json, the channel specs in examples/channel_*.vl.json (empty data, all-null column, single-value domain, log scale with non-positive values), and six charts generated by MoonFrame (examples/moonframe). Invalid JSON and unsupported features are covered by vl/errors_test.mbt.

moon test passes on wasm-gc, js and native.

Fixture provenance and regeneration

Fixtures are MoonBit files generated by scripts/gen_fixtures.mjs with the versions pinned in scripts/package-lock.json (vega-lite 5.23.0, vega 5.33.1, vega-datasets 3.2.1) on Node.js 24 (some expected values depend on V8’s Math functions, so the Node major version matters). The process time zone is fixed to UTC.

cd scripts
npm ci
npm run fixtures            # downloads the example specs of the vega-lite tag, regenerates ../fixtures
npm run gallery             # regenerates ../gallery (needs: moon build --target native cmd/vl2svg)

CI regenerates the fixtures and fails if the committed files differ. The one exception is util/pow10_table.mbt (V8’s Math.pow(10, k)): regenerated on the Linux runner it differs from the committed file, which was generated on Windows, so it is not compared there (the cause was not investigated further).

examples/moonframe/*.vl.json were produced by ihb2032/MoonFrame 0.7.0 (@chart.format_vega_lite). MoonFrame 0.7.0 does not currently build with the latest MoonBit toolchain (its dependencies NyaCSV and quickcheck still ship moon.mod.json, and three NyaCSV tests use a removed Buffer API), so the specs were generated in a scratch project after migrating those manifests and removing the three tests locally. The generated JSON is committed unchanged.

Layout

Package Content
util, stats, format, time, value JavaScript-compatible numbers (V8 libm), d3 ticks, d3-format, time intervals / formats, dynamic values
scale continuous, band, point, ordinal and sequential scales, colour schemes
expr, transform the expression subset, aggregate / bin / stack / timeunit / fold / filter / formula
scene, svg scenegraph, mark bounds, path generation, SVG renderer
spec strict reader of Vega-Lite JSON (unsupported features raise errors with paths)
vl the compiler: data pipeline, scales, axes, legends, layout
cmd/vl2svg command line tool (native)

License

Apache-2.0. Parts of the code are ports of vega, vega-lite and d3 (BSD-3-Clause / ISC); see the file headers.

    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号