Skip to content

v0.23.0

Choose a tag to compare

@sipemu sipemu released this 22 Oct 10:10
6c78d0b

Minor bump: per-panel dodge/jitter, negative stacking, legend wrap for
grids/multiple guides, vertical legend fit. Default output stays byte-
identical except for the deliberate changes under "Changed", each guarded
by a test.

Added

  • DrawBackend::draw_text_with_title (the default draws the text and
    drops the title; the native SVG backend writes the <title>) and render::text::truncate_to_width (a new pub fn in the public render::text module): draws (or measures) a text
    run with an attached full-text <title> tooltip, used by #159's label
    truncation below to keep a truncated label's full text available via
    hover even though the drawn text itself is shortened. The truncated
    label is a plain text run, not a gg-mark: an SVG consumer that treats
    "has a <title>" as "is a data mark" should select on gg-mark instead.
  • Position::group_order (default None) and
    PositionParams::group_order: the layer-wide group order a faceted
    dodge/dodge2 ranks its groups by (#153, see Changed).
  • tests/default_output_023.rs, on the same pattern as
    tests/default_output_022.rs: a battery of fixed plots exercising
    #153/#154/#155/#159's deliberate changes plus their byte-identical
    unaffected counterparts, hashed with FNV-1a. Wired into CI's sf,
    no-default-features test job alongside default_output_022. Extended
    (review #167) with a PositionFill mixed-sign case alongside
    PositionStack's, and tests/position_panels_r.rs gained a dodge-
    order case with two panels listing their groups in opposite row order
    (#153 finding 2), and tests/legend_wrap.rs gained a PlotGrid
    collected-legend case with two simultaneous discrete guides (#155
    finding 1).

Breaking

  • PositionParams has a new public field, group_order: Option<Vec<String>> (#153). Code that builds PositionParams with a
    struct literal must add group_order: None (or use
    ..Default::default()). cargo semver-checks flags this as
    constructible_struct_adds_field; it's the only API break.

Deprecated

  • guide::legend::draw_legend is deprecated (#[deprecated(since = "0.23.0")])
    and will be removed in a later minor release: it's unused internally as
    of this release — #155's rerouting of compose::capture_legend to the
    crate-internal draw_legend_planned (so a PlotGrid's collected legend
    gets the same wrap/grid treatment a single plot's legend does) removed
    draw_legend's only caller. There is no direct public replacement
    (draw_legend_planned is pub(crate)); legends are drawn automatically
    by GGPlot's own render methods, which was always the supported way to
    get a legend onto a plot.

Changed

  • A faceted position_dodge/position_dodge2 now computes each panel's
    group count/offset from that panel's own rows instead of the rows
    pooled across panels (#153), matching ggplot2 4.0.3 (verified with
    layer_data(): a group missing from one panel's x level no longer
    shifts the groups that panel does have). Group rank — which group
    dodges left vs right — still comes from the whole layer's first-
    appearance order (review #167 finding 2), not from each panel's own
    row order, so every panel agrees, as in ggplot2 4.0.3. (ggplot2 ranks by
    the sorted group id, so the two still differ when the data's first
    appearance order isn't sorted; 0.22.2 behaved the same.) A panel
    whose rows happen to list two groups in a different order than
    another panel still draws a given group on the same side in both. A
    faceted position_jitter's default width/height (resolution(x) * 0.4) is now likewise measured per panel — also verified with
    layer_data(): two panels with very different x spacing get
    differently-scaled jitter even under shared, fixed facet scales. A
    plot with 2+ distinct facet panels and a dodge or jitter layer may
    render differently; an unfaceted plot, or one with a single panel, is
    unaffected (tests/position_panels_r.rs, including
    facet_dodge_order_is_global_not_per_panel_first_appearance).
  • position_stack/position_fill now split a group's positive and
    negative values at each x into two independent stacks instead of one
    pooled running total (#154), matching ggplot2's internal pos/neg
    split (collide/pos_stack): positives stack upward from 0 using only
    the positive sum as the total (or position_fill's normalisation
    denominator), negatives stack downward from 0 using only the
    (absolute) negative sum, each with its own vjust anchoring, verified
    against ggplot2 4.0.3 with layer_data() (a 4-group bar at one x with y = 3,
    -2, 4, -1: position_stack(vjust = 0.5) gives y/ymin/ymax (5.5, 4, 7),
    (-2, -3, -1), (2, 0, 4), (-0.5, -1, 0); position_fill(vjust = 0) gives
    (4/7, 4/7, 1), (-1, -1, -1/3), (0, 0, 4/7), (-1/3, -1/3, 0)). All-
    positive (or all-negative) data at an x is byte-identical — the other
    sign's bucket is simply empty (src/position/stack.rs,
    src/position/fill.rs unit tests).
  • A plot's auto-wrap/grid legend layout (0.22) now applies to every
    simultaneous discrete guide independently, not only when the plot has
    exactly one (#155): two discrete guides (e.g. colour and shape both
    mapped to discrete variables) laid out side by side each wrap against
    an equal share of the available width, and a vertical explicit
    nrow/ncol grid on either reserves its own extra width. Because
    GuideLegend (the .guides(...) argument) is a single, global
    setting, an explicit nrow/ncol on it now applies to every
    discrete guide, including one sitting next to a continuous guide
    (e.g. a colourbar plus a discrete shape) — a discrete guide next to
    a continuous one now participates in auto-wrap/grid too. In 0.22 an
    explicit nrow/ncol was silently ignored as soon as the plot had
    two or more legend scales of any kind, discrete+discrete or
    discrete+continuous. A PlotGrid's collect_legends(true) strip also
    now wraps its collected legend against the grid's own page size,
    instead of always drawing the plain, unwrapped path — previously the
    only legend eligible for wrap/grid was a single plot's single discrete
    guide, rendered directly (not collected into a grid strip); two
    simultaneous discrete guides collected this way are laid out side by
    side, each against its own equal share of the page width, the same as
    a single plot's legend (review #167 finding 1 — capture_legend
    measures the collected legend against a fake zero-width panel, so the
    step to the next guide must use the share plan_all_legends actually
    planned against, not that panel's own width). A plot with one discrete
    guide that already fit is unaffected (tests/legend_wrap.rs's
    two_discrete_guides_each_auto_wrap,
    plotgrid_collected_legend_auto_wraps and
    plotgrid_collected_legend_two_guides_sit_side_by_side cover the new
    cases).
  • A vertical legend's explicit ncol grid that still runs off the canvas
    after reserving (cols - 1) * col_pitch (0.22's "warn only" check) now
    tries harder before giving up (#159): it first reduces the column
    count toward 1, keeping the first count that fits; if even one column
    overflows, it truncates labels with an ellipsis instead (the full label
    stays available via a <title> tooltip on the label text, escaped the
    same way every other text run is — DrawBackend::draw_text_with_title, new,
    default no-op, SVG-only); only if it still doesn't fit after both does
    it warn, as before. A grid that already fits renders byte-identical to
    0.22.2 — no reduction, no truncation, no warning
    (tests/legend_wrap.rs's a_vertical_grid_that_already_fits_is_byte_identical,
    plus the reduce/truncate/still-warns cases replacing
    vertical_legend_ncol_grid_too_wide_for_the_canvas_warns, which
    asserted the old "always warns" behaviour).