Repository navigation
v0.21.0 — SVG mark contract
Minor bump: the SVG mark contract. Default output is byte-identical except
for four deliberate changes: fill-opacity/stroke-opacity omitted at
1.000, draw_line splitting into separate <polyline>s at non-finite
vertices, every data mark gaining a gg-mark gg-<kind> class, and GeomText
honouring a theme/geom font family it previously ignored. Layer and
Theme/ThemeUpdate gain new public fields, so struct literals without
..Default::default() need them added.
Added
- SVG data marks carry a stable class: every element a layer draws gets
class="gg-mark gg-<kind>"(gg-point,gg-line,gg-band,gg-bar,
gg-text, by primitive) anddata-geom="<geom name>", so a host can select
marks without matching grid lines, axes or legends, which stay unclassed
(#121). While a mark part is set (e.g. an arrow head), the part is the
effective kind, so arrow heads carrygg-mark gg-arrowrather than their
parent'sgg-band/gg-line.data-geomon reference lines readshline,
vlineorabline. NewDrawBackend::set_mark_geomhook (default no-op),
forwarded bycompose::Measureas well as the panel backend adapter, so
PlotGridmarks are classed too. - New default
Geom::keeps_na_as_breakhook (defaultfalse;GeomLine/
GeomPath/GeomStepreturntrue): a row whose position is non-finite is
no longer deleted from the built data for these geoms, sodraw_line's
per-vertex split (below) actually has a gap to split on, instead of the
row being spliced out and its finite neighbours silently joined across it. GGPlot::svg_attr(name, value)/svg_attrs(iter)— and the same API on
PlotGrid— add caller attributes to the root<svg>:data-*/aria-*
(the same charset-restricted suffixAes::data_attruses), or the fixed
namesrole/id/class/lang. Anything else, or a name ggplot-rs
already writes to the same root, is dropped with a build warning rather
than silently overwriting or duplicating it.GGPlot::svg_warnings_attr(bool)(and onPlotGrid) emitsdata-warnings
as a JSON array of the build warnings,[]when there are none.- Per-mark formatted values: new default
Scale::format_value(&Value) -> Option<String>, overridden byScaleContinuous(with_value_formatter,
falling back to the label formatter then the locale, and inverting the
scale's own transform first —scale_y_log10'sdata-fvalueshows
"100", not the transformed-space"2"),ScaleDateTime(with_date_ labels, else the locale date format, else plain ISO — the same order
the axis break labels already use), and the size/linewidth/colour-
continuous scales (their own formatter).GGPlot::svg_formatted_values (bool)(default off) turns ondata-fvalue(the value aesthetic's
scale — y for most geoms, fill for hex/tile/bin2d) anddata-fx(the x
scale, written only when it differs from the plaindata-x) on marks.
NewDrawBackend::wants_formatted_values/set_mark_fvalue/
set_mark_fxhooks (all default no-ops). A vline/hline'sdata-xand
tooltip show the matching scale's formatted value too (e.g. an ISO date
on a datetime x) when the flag is on. GGPlot::tooltip(template): a{column}tooltip template for the most
recently added layer, replacing its default<title>. Each placeholder
is filled from the built (post-stat) layer data — so it can reference
a stat-computed column, e.g. a hex'scount— formatted through the
column's scale when it has one. An absent column renders empty, not
literally{column};{{/}}are literal braces.- Geom text inherits the theme font:
GeomText/GeomLabel/GeomTextRepel/
GeomLabelRepel/GeomBracket/text annotations resolve their font family
as the geom's ownfontfamily(previously a dead field onGeomText/
GeomLabel), elsetheme.geom.family, elsetheme.text.family, else
"sans-serif". #[non_exhaustive] ElementGeom { ink, paper, accent, family }(ggplot2
4.0'selement_geom), plusTheme.geom,ThemeUpdate.geom,
Theme::set_geom/with_ink_paper, andThemeConfig'sgeom.ink/
geom.paper/geom.accent/geom.familyconfig keys. A new default
Geom::apply_element_geom(a no-op) is called once per layer before the
brand/primary colour, and only for a layer not configured explicitly
viageom_*_with(...)(the sameexplicit_stylegate the brand colour
already uses — review PR#141 finding 4: a field-value comparison
against the hard-coded default can't tell an explicitly-set colour that
happens to equal it apart from one nobody touched, so the gate skips the
call entirely instead). Covered geoms:GeomPoint/GeomLine/
GeomPath/GeomText/GeomLabel/GeomBoxplotrecolour their built-in
black/(50, 50, 50)/white defaults fromink/paper, andGeomSmooth
fromaccent. The legend key swatch colour is now
theme.geom.ink.unwrap_or((50, 50, 50)). Every other geom (segment,
curve, reference lines, errorbar/linerange/pointrange, step, rect/tile
outlines, density/violin/area outlines, crossbar) does not yet honour
ElementGeom— tracked at #142.ScaleColorContinuous/ScaleColorGradient2/ScaleColorGradientNgain
with_limits(lo, hi), fixing the colour domain (not just the legend) to
a range independent of the trained data — e.g.[-1, 1]for a
correlation matrix. NewOobenum (Censor, the default: an
out-of-range value renders in the scale'sna_value;Squish, opt-in
viawith_oob: clamped to the nearest limit).- The same three scales gain
na_value(default(127, 127, 127),
ggplot2's"grey50") and awith_na_valuebuilder: a genuinely missing
(NA) or non-finite value, and a value censored byOob::Censor, all
render inna_valueinstead of a bareNonethe geom silently papers
over with its own unrelated default colour. - New default
Geom::consumes_tooltipshook (defaultfalse; the 13
geoms that actually read a per-row tooltip — bar/col/histogram/hex/
line/path/point/polygon/rect/sf/segment/curve/tile — returntrue):
GGPlot::tooltip(template)on a layer whose geom ignores tooltips now
pushes a build warning instead of silently having no effect.
Fixed
Scale::format_valueonScaleContinuousnow inverts the scale's own
transform (scale_x_log10, etc.) before formatting:data-fvalueand a
{y}tooltip-template placeholder on a log-scaledy = 100showed"2"
(the transformed-space value the built data actually holds) instead of
"100", while the axis labels correctly showed the inverse already.ScaleDateTime::format_valuenow honourswith_date_labels(falling
back to the locale date format, then plain ISO) instead of always
ISO/locale regardless of it — the axis break labels already did. This
also un-breaksdata-fxon a datetime x withwith_date_labelsset: it
was suppressed as "equal todata-x" because the un-formatted ISO
fallback happened to be the only thing compared.PlotGridcells each honour their ownsvg_formatted_valuesflag
(previously only a top-levelGGPlot::render_svg_native*call applied
it; a plot composed into a grid lostdata-fvalue/data-fxeven with
the flag set on that cell).ScaleColorContinuous/ScaleColorGradient2/ScaleColorGradientN's
with_limits(lo, hi)withlo > hi(e.g. a typo) no longer panics
underOob::Squish(f64::clamppanics whenmin > max) — every
value is treated as censored (na_value) regardless of the configured
Oob, same asOob::Censoralready did, andScale::validatenow
pushes a build warning naming the inverted limits.- The root/
PlotGridsvg_attrcollision warning now distinguishes a
name ggplot-rs itself writes (data-plot,data-grid, …) from a
secondsvg_attr/svg_attrscall for the same name — the latter
previously said "collides with an attribute ggplot-rs already writes
to the root", which wrongly blamed ggplot-rs for the caller's own
earlier call; it now says "was already set by an earlier svg_attr
call".
Changed
- Missing colour values render grey on continuous colour/fill scales (
na_value, default grey50, as in ggplot2), also without limits. Previously NaN rendered black and NA the low end of the gradient. fill-opacity/stroke-opacityis written only when the value isn't
1.000(the SVG default); it was previously written unconditionally on
every mark.draw_linesplits at a non-finite vertex into separate<polyline>
marks instead of silently dropping the bad vertex and joining its finite
neighbours across the gap (ggplot2's NA-break semantics) — for
geom_line/geom_path/geom_steponly (seekeeps_na_as_breakabove).
A run shorter than 2 points is dropped, the same as ggplot2 drops a
one-observation path piece, rather than becoming an invisible 1-point
polyline mark.GeomStepclamps only an infinite pixel coordinate to
the panel edge (stat_ecdf's±Infpadding); aNaNcoordinate is left
alone so it breaks the line instead of being clamped to an edge it was
never meant to reach."geom","fvalue"and"fx"join"part"as reserveddata_attr
names (alongside the attributes they already name): a mapping under any
of these is rejected with a build warning, the same as"part"already
was, so no element can ever carry twodata-geom/data-fvalue/data-fx
attributes.
Breaking
Layergains a publictooltip_template: Option<String>field.Themegains a publicgeom: ElementGeomfield;ThemeUpdategains a
publicgeom: Option<ElementGeom>field. Struct literals for either
without..Default::default()need the new field added.