Skip to content

vellumwidget 0.6.0

Choose a tag to compare

@schochastics schochastics released this 20 Jul 08:43
· 23 commits to main since this release
  • Bug fix: legend swatches no longer jump off-screen under axis-aware zoom.
    With axis_zoom/zoom_marks = "fixed" (both default), the constant-size counter-
    scale was applied to every glyph mark, including discrete legend swatches. Because
    a swatch is positioned by a transform attribute (unlike data points, which carry
    absolute coordinates), the inline counter-scale overrode that attribute and flung
    the swatch to a corner. The counter-scale is now restricted to marks inside the
    pannable data region, so legend swatches stay put.

  • Axis-aware zoom (axis_zoom, default TRUE). Wheel/drag zoom scales only
    the plot's data region and re-ticks the axes for the visible range — holding the
    frame (axes, titles, legend) in place the way a charting library zooms, rather
    than scaling the whole scene like an image. Hit-testing (hover/brush/lasso), the
    crosshair, and the input$<id>_zoom data range all follow the zoomed data region.
    Applies to a single linear cartesian panel (continuous identity/reverse
    axes) rendered as SVG; plots with log/date/discrete axes, several panels, or in
    raster mode silently fall back to the ordinary whole-scene zoom, so it is safe to
    leave on. Set axis_zoom = FALSE for the plain whole-scene zoom. Built on vellum's
    pannable-panel contract (vl_viewport(pannable=)) and the panel scale metadata
    vellumplot emits (needs the current development vellum/vellumplot).

  • Constant-size markers on zoom (zoom_marks, default "fixed"). Under
    axis-aware zoom, glyph marks (points, circles, hexagons, sector wedges) now keep
    their original pixel size and only their positions re-map — so points stay round
    and don't stretch into ellipses under the navigator's x-only zoom, the way a
    charting library zooms. Positional marks (bars, error bars, lines, areas) still
    scale with the data; their stroke width is held constant. Set zoom_marks = "scale" for the old behaviour where glyphs grow with the zoom (useful to read
    density). Applies to SVG axis-aware zoom and the raster crisp-point layer.

  • Interactive continuous colorbar filter (visualMap). When a vellumplot plot
    maps a continuous color scale, its colorbar becomes a range filter: drag the
    two handles on the gradient bar to a value range and marks whose colour value
    falls outside it fade out (and drop out of hover/brush hit-testing); double-click
    the bar to reset. The selected range is reported to Shiny as
    input$<id>_colorfilter = c(lo, hi) (NULL at the full range). Automatic — no
    argument to set — whenever the plot has a continuous colorbar (needs the current
    development vellumplot; SVG mode). Discrete/binned colour legends keep their
    existing click-to-hide interaction.

  • Tooltip polish. as_widget() gains tooltip_delay (ms to wait before the
    tooltip appears — the highlight is immediate, only the tooltip waits),
    tooltip_follow (TRUE, the default, tracks the cursor; FALSE anchors above
    the mark), and tooltip_sticky (the tooltip accepts pointer events and lingers
    briefly on leave, so tooltips containing links/buttons are usable). The tooltip
    also now auto-flips below the cursor when there isn't room above and clamps
    horizontally so it doesn't overflow the widget.

  • Overview navigator (navigator = TRUE). An opt-in strip below the plot that
    renders the whole scene in miniature with a draggable, resizable window marking
    the visible x-range: drag the window to pan, drag a handle to zoom. Zoom is
    x-only — the selected x-range fills the width while the full y-range stays on
    screen (a time-series range selector). With axis_zoom (the default) it is
    rendered through the axis-aware zoom, so the x-axis re-ticks crisply; otherwise
    the view stretches horizontally. It stays two-way in sync with the main view —
    wheel/keyboard/brush, the toolbar, and linked-group pan/zoom all move the window,
    and moving the window drives them. Useful for scrubbing long series.
    navigator_height sets the strip height (default 56px). Client-side; off by
    default.

  • Linked pan/zoom across a group. Widgets sharing a group already linked
    selection and hover; now panning or zooming one moves the others to the same
    view. The view is shared as a fraction of each widget's own extent (over the
    same client-side bus, no Shiny/crosstalk), so linked plots of different sizes —
    small multiples — stay aligned. Reset links too.

  • Brush and view now report data-space coordinates, not just pixels. When the
    plot carries a cartesian scale (any vellumplot plot), input$<id>_brush gains
    the brushed region's data-space bounds x0d,y0d,x1d,y1d (plus the panel name)
    alongside the existing device-pixel rectangle, and input$<id>_zoom gains
    data = list(x=, y=, panel=), the visible range in data coordinates. This reads
    the per-panel scale descriptors vellumplot now attaches to the scene (requires
    the current development vellum (>= 0.4.0.9000) and vellumplot); a raw vellum scene
    or a non-cartesian coordinate system reports device-pixel fields only, as before.
    Date/time axes report the numeric epoch (days for Date, seconds for POSIXct),
    which you map back with as.Date() / .POSIXct(). The fields describe the
    visual axes (under coord_flip(), x0d is the plot's y aesthetic); a
    discrete axis reports fractional band positions; and an axis built with a custom
    scales::transform_*() object (beyond identity / log10 / sqrt / reverse) is
    omitted from the data-space fields rather than reported wrong. (Groundwork for
    axis-aware zoom.)

  • Fix: cross-filtered and legend-hidden marks were still hit-testable. A
    display-tier cross-filter (crosstalk or vw_filter()) and a legend
    legend_click = "hide" toggle set the marks to display:none, but they stayed
    in the spatial index — so a nearest-mark hover could still tooltip a hidden
    datum, and a brush or lasso could re-select filtered-out points (propagating the
    selection back to linked/crosstalk views). Keyboard traversal already skipped
    hidden marks; hover, brush, lasso, and raster click-snap now do too, via a single
    "inert" guard. Muted (not hidden) legend series stay interactive.

  • Freehand lasso-select. A third drag mode alongside brush and pan (default
    on; disable with as_widget(lasso = FALSE)). The toolbar's mode button now
    cycles brush → lasso → pan, and it appears whenever at least two drag modes are
    enabled. Drag a loop and every mark whose centre falls inside it is selected —
    hit-tested with a point-in-polygon check over the Flatbush spatial index, so it
    stays fast on large scenes. It reports through input$<id>_brush like the box
    brush, with a lasso = TRUE flag and the loop's bounding box.

  • Widgets report their current view to Shiny (input$<id>_zoom). After a
    zoom/pan settles — wheel, drag-pan release, pinch, keyboard, reset,
    zoom-to-selection, or a vw_zoom() proxy call — the widget publishes
    list(x=, y=, w=, h=, zoomed=): the current viewBox (device-px) and whether
    the view is zoomed in. A deduped state input, so a coordinated dashboard can
    react to what the user is looking at. (Data-space limits await axis/scale
    metadata in the scene contract.)

  • Legend click-to-hide and double-click-to-isolate. as_widget() gains a
    legend_click argument. The default "select" is unchanged (clicking a
    discrete-legend swatch selects its series). "hide" turns the legend into a
    visibility toggle — a single click hides or shows the series, a double-click
    isolates it (hides every other series; double-click again to restore all) — the
    reflexive legend interaction from plotly / ECharts / Highcharts. "mute" is the
    same but dims the series rather than removing it (keeping its layout). Hovering a
    swatch still highlights its series under every policy, and hidden series drop out
    of hover/tooltip hit-testing. Works with keyboard (Enter/Space on a focused
    swatch toggles) and is independent of the crosstalk cross-filter (the two never
    clobber each other). Entirely client-side.

  • Shared (unified) hover tooltips and a crosshair. as_widget() gains a
    hover_mode argument. The default "closest" is unchanged (hover shows the
    single nearest mark), but "x" (or "y") turns on a unified hover: every
    mark sharing the hovered x (or y) position is highlighted at once and listed
    together in one tooltip box — the shared readout multi-series line and
    time-series charts expect, where you want every series' value at the cursor's
    x rather than one point. A companion crosshair argument (default FALSE)
    draws a guide rule at the hovered position: a vertical rule at the shared x in
    "x" mode, a horizontal one in "y" mode, and a full cross through the mark
    in "closest" mode. Both are entirely client-side and need no extra scene
    metadata — the mark positions come straight from the element index — so they
    work on any keyed vellumplot plot or raw vellum scene, and in raster mode.
    Unified hover snaps along its axis so the readout tracks the cursor anywhere in
    the plot; the hover Shiny input still reports the single nearest (primary)
    mark, so the read-back contract is unchanged.

  • Fix: hover/selection feedback rings were drawn offset from the mark. When
    the widget container was larger than the plot's rendered box — which happens
    routinely, as htmlwidgets stamps an explicit height on the container and a
    fluid layout can stretch its width — the feedback overlay filled the whole
    container while the base SVG only filled its aspect-locked box. The overlay's
    viewBox then letterboxed (centred), so every hover/selection ring drew off
    the real mark: downward when the container was taller (e.g. 68px low for a
    520px-tall container over a 384px plot) and sideways when it was wider. It
    looked like the wrong mark was picked. Hit-testing itself was always correct;
    only the visual feedback was displaced. The svg and its overlays now share a
    shrink-to-fit stage, so the overlay tracks the svg box exactly at any container
    size.