Skip to content

Releases: physwkim/rsplot

0.6.1 — storage params blocks, colours in the target's encoding

Choose a tag to compare

@physwkim physwkim released this 17 Oct 15:20

Fixed

  • rsplot: every per-draw params block — the curve pipelines (line,
    markers, caps, fill, error bars), the data-area clear, the image tiles, the
    3D scene pipelines and the volume ray-caster — is bound as a read-only
    storage buffer instead of a uniform. On Windows' DX12 backend with the WARP
    software adapter (a GPU-less VM) every curve in a pass read the last
    curve's uniform, so all curves took the last curve's colour; storage reads
    were per-curve correct. The block layouts are unchanged.
  • rsplot: curve, per-vertex, dashed-gap and data-background colours are
    uploaded in the render target's encoding: sRGB bytes for a non-sRGB target
    (egui-wgpu's swapchain), linear for an sRGB one. They were always
    linearised, which on the non-sRGB target drew every colour darker than its
    Color32 and than egui's own legend swatches (#4f9bff showed as
    (20, 84, 255)); PNG export was affected the same way.
  • rsplot: the 2D image path uploads its colour texels in the render
    target's encoding too: the colormap LUT and direct-RGBA tiles are
    Rgba8Unorm for a non-sRGB target and Rgba8UnormSrgb for an sRGB one,
    and the colormap's nan_color follows the same rule. They were always
    sRGB-decoded to linear, which on egui-wgpu's non-sRGB swapchain drew every
    colormapped image and RGBA image darker than its colormap (viridis' mid
    teal (33, 145, 140) showed as (4, 71, 66)).
  • rsplot: the 3D scene (paint_scene3d, SceneWidget, ScalarFieldView,
    the volume cut plane and iso-surfaces) is rendered into the sRGB variant of
    the surface format and the blit encodes the linear result to sRGB when the
    surface is non-sRGB, so geometry, mesh shading, fog, image layers and the
    background now show at their Color32 values on egui-wgpu's swapchain
    instead of darker. snapshot_scene3d / SceneWidget::snapshot read back
    the sRGB offscreen target, so their PNGs carry sRGB-encoded bytes whatever
    the surface format (they were as dark as the screen).
  • rsplot: VolumeRaycaster uploads its voxel colours in the render
    target's encoding — the sRGB bytes as is for egui-wgpu's non-sRGB swapchain
    (unchanged there), linearised for an sRGB target, where the composite used
    to be encoded a second time on store and showed washed out (an opaque
    (255, 128, 0) voxel read back as (255, 188, 0)).

0.6.0 — egui 0.36 / wgpu 30, deferred GPU sync, form helpers

Choose a tag to compare

@physwkim physwkim released this 16 Oct 04:12
345dc65

Changed

  • egui / egui-wgpu 0.36 (wgpu 30), MSRV 1.95. rsplot re-exports
    egui, so downstreams move to 0.36 with it — hence the minor bump. Adapted:
    wgpu 30's Option vertex buffers and fallible get_mapped_range, egui's
    Panel::show_inside → show, egui_kittest::create_render_state taking
    RendererOptions; the headless widget tests run through
    test_util::HeadlessRun, since epaint 0.36 panics when an unapplied
    TexturesDelta is dropped.
  • rsplot: WgpuBackend defers sync_gpu_items to one flush_scene per
    frame, so N item mutations (add / remove / update, z, visibility, clear)
    rebuild the GPU scene once instead of N times; unchanged z / visibility
    writes no longer dirty the scene.
  • rsplot: the title and the x, y, y2 and extra-axis labels lay out
    single-line and elide with … to their gutter run instead of being clipped
    mid-glyph on a narrow plot; overlapping tick labels are culled on every
    gutter (tick marks never).
  • rsplot: the compact-toolbar overflow button uses U+2026, which egui's
    bundled font covers (U+22EF rendered as the tofu box).

Added

  • rsplot: widget::form — label / fill / group helpers for Qt-style
    (QFormLayout) dialog rows in one egui pass; the colormap, print and
    limits dialogs use them.
  • rsplot: a whole-widget plot background: set_background_colors' first
    argument was dropped, so a light canvas over a dark host panel got dark
    tick labels on still-dark gutters. When set, PlotView paints the widget
    rect with it under the data layer.
  • Documented that hosts must append a broad-coverage fallback font for the
    arrow and box-drawing glyphs the ROI descriptions, the style selector and
    the image-compare toolbar use.

Fixed

  • rsplot: a range ROI's name label stays inside the data area instead of
    landing in the title gutter over the plot title.

Full changelog: v0.5.6...v0.6.0

0.5.6 — drop the printers crate (no CUPS build dep)

Choose a tag to compare

@physwkim physwkim released this 19 Jul 12:00

Changed

  • rsplot: the toolbar Print action no longer depends on the printers
    crate, so a default build links no CUPS library (-lcups) and needs no
    libcups2-dev at build time. Printer enumeration and job submission now shell
    out to the lpstat / lp command-line tools. Printing works wherever those
    are on PATH (CUPS on Linux/macOS); on platforms without them (e.g. Windows)
    the printer list is empty and the print action reports no destination
    (print_graph returns Ok(false)). The public print_graph /
    print_graph_to signatures are unchanged.

Removed

  • rsplot: the printers crate dependency (and its transitive CUPS /
    libcups build requirement).

0.5.5 — EPICS backends on epics-rs 0.24

Choose a tag to compare

@physwkim physwkim released this 18 Jul 00:09

Changed

  • Bumped the EPICS backends epics-ca-rs / epics-base-rs / epics-pva-rs
    to 0.24.0
    (from 0.22.x), affecting rsdm's ca/pva/calc features
    only. Three breaking API changes were adapted:
    • ConnectionEvent::Unresponsive was removed — 0.24 folds the echo-timeout
      into Disconnected (an unresponsive circuit disconnects every consumer; the
      distinction now survives only inside the client's ChannelState). rsdm
      already handled both identically.
    • The pvAccess RPC reply became RpcReply (an enum) instead of a
      (descriptor, value) tuple; its Empty variant is pvxs's no-value
      ExecOp::reply(). rsdm reads the value through RpcReply::into_value,
      treating Empty as the existing "connected, no scalar sample" case.
    • EnumInfo became #[non_exhaustive] (it gained a string_form); a test
      constructs it via EnumInfo::new instead of a struct literal.

0.5.4 — per-crate README

Choose a tag to compare

@physwkim physwkim released this 16 Jul 01:33
3036b9e

A documentation-only patch release. No code, API, or behaviour changes.

Added

  • rsdm and adl2rsdm now carry a README, which crates.io renders on each
    crate's page. Both crates previously shipped without a readme field or a
    README file, so their crates.io pages showed nothing while rsplot's
    rendered normally. crates.io bakes the README per published version, so this
    is why a new release was cut rather than an edit to 0.5.3.

0.5.3 — Round-4 parity fixes

Choose a tag to compare

@physwkim physwkim released this 12 Jul 11:03
b5c2258

A regression release. A workspace-wide upstream-parity audit (silx / PyDM /
adl2pydm + MEDM C) found that three of the fixes 0.5.2 shipped were wrong, and
turned up ten further divergences from the reference behaviour. All are fixed
here. VolumeRaycaster users should upgrade: the volume it rendered in 0.5.2
carried no thickness cue and lost the hue of faint voxels.

Fixed — rsplot

  • Volume opacity depends on the thickness a ray crosses again. 0.5.2's
    "Beer-Lambert" correction divided by the step count, an exponent carrying no
    world distance, so every ray accumulated the same transmittance whatever chord
    it traversed: a uniform volume rendered as a flat silhouette, and at the
    default 256 steps the correction was exactly the identity. Each sample is now
    corrected from a reference spacing (box_diagonal / 256, a world distance),
    which keeps the step-count invariance 0.5.2 was after while restoring the
    density cue. A ray crossing the full box diagonal at the default step count
    renders exactly as before.
  • Faint voxels keep their colour. The premultiplied 3-D texture 0.5.2
    introduced stored the product rgb · a in 8 bits, and the shader must divide
    the coverage back out to recover the straight colour — a product that at
    coverage a resolves the quotient only to steps of 1/a. An authored
    (255, 128, 0) voxel at a = 3/255 read back as (255, 170, 0); at
    a = 1/255 no hue survived at all. The texture is now Rgba16Float, which
    carries the straight colour exactly at every coverage while the filter still
    interpolates premultiplied colour (no return of the dark fringe). This
    doubles the volume texture's VRAM.
  • VolumeRaycaster::remove no longer blanks another view. Two views sharing
    a VolumeId share one texture, by design; remove freed it outright, so the
    survivor silently rendered nothing. The texture is now claim-counted and lives
    exactly as long as the views holding its id. Dropping a view releases its
    claim too, so a VolumeRaycaster that is simply dropped no longer pins its
    VRAM for the app's lifetime.
  • Alt+Wheel zooms X only and Shift+Wheel zooms Y/Y2 only, the silx bindings
    the axes menu already advertised. Plain wheel and keep-aspect are unchanged.
  • Middle-button drag pans in every interaction mode, as in silx. The
    existing right-button pan is kept.
  • The Reset-Zoom toolbar button disables when neither axis autoscales, and
    its tooltip names the single autoscaling axis when exactly one is on.
  • Programmatic set_limits and toolbar Zoom-In/Out repair their limits.
    Inverted, degenerate or float32-overflowing ranges reached the transform
    unrepaired; every view-limits commit now runs the same clamp, including the
    Y2 and extra-axis paths.
  • The colormap dialog's histogram drops samples equal to the range maximum,
    as Histogramnd does with its default half-open last bin.
  • Scatter BINNED_STATISTIC drops points on the max edge instead of
    clamping them into the last bin, matching scipy.stats.binned_statistic_2d
    as silx calls it. A degenerate extent now admits no point rather than
    collapsing every point into bin 0.

Fixed — rsdm

  • calc:// re-evaluates when a child connects or disconnects. PyDM
    re-emits on a connection-state change; rsdm only watched values.
  • calc:// no longer retriggers on a child's alarm or metadata change, only
    on an actual value change, as PyDM's update list specifies.
  • A dialect = medm expression reading operand I or J re-evaluates on an
    alarm status / severity change.
    MEDM's setDynamicAttrMonitorFlags adds
    monitorStatusChanged and monitorSeverityChanged exactly when the
    expression uses those operands. rsdm never watched severity, so a transition
    left the calc showing a stale value, and it had no alarm status to read at all
    — operand I bound a constant 0.0. Both are now monitored, and only when
    the expression reads them.

Added — rsdm

  • ChannelState::status carries the wire alarm status — the record's STAT
    for ca://, the pvAccess alarm.status enum for pva://. It backs the MEDM
    calc operand I.

Fixed — adl2rsdm

  • Embedded-display and composite-file children take the host screen's
    colormap.
    MEDM's parseCompositeFile parses and discards the child file's
    "color map" block and resolves the children's clr/bclr against the
    parent display's palette; adl2rsdm used the child's own. Screens on the
    default palette are unaffected; those with a custom palette rendered every
    embedded widget in the wrong colours.
  • An arc with no path spans 90°, not 360° — MEDM's createDlArc default.

Changed — rsplot

  • The volume 3-D texture is Rgba16Float rather than Rgba8Unorm, doubling its
    VRAM. See the faint-voxel fix above for why an 8-bit texture cannot carry the
    data.
  • VolumeRaycaster now holds a clone of its RenderState (a bundle of Arcs)
    so it can release its texture claim on drop.

v0.5.2

Choose a tag to compare

@physwkim physwkim released this 08 Jul 03:31
ceca5de

Patch release over 0.5.1. A correctness and robustness pass over the
VolumeRaycaster, plus two rsdm data-engine race fixes surfaced by the
cross-platform CI. adl2rsdm is re-released in lockstep.

Fixed — rsplot (VolumeRaycaster)

  • Dark fringe around opaque regions gone (premultiplied-alpha compositing).
  • Opacity no longer depends on the ray step count (Beer–Lambert normalized).
  • set_steps clamped to [1, 4096] (GPU-timeout watchdog safety).
  • Invalid volumes rejected up front (extent + RGBA-length validation).
  • Ctrl/Cmd-pan tracks the pointer one-to-one (anchored at box-centre depth).
  • Same VolumeId painted twice in one frame keeps each camera (per-callback uniforms).

Changed — rsplot

  • VolumeRaycaster::remove / remove_volume_raycaster(id) free a volume's GPU resources.
  • Dropped the unused cam_pos uniform.

Fixed — rsdm

  • ca:// no longer leaks a duplicate initial sample when the connect handler re-enters (native-type-change on first connect); the connect-time value is gated on an actual change.
  • calc:// always publishes its initial derived value regardless of child connect order (stamp changes folded in only once every child is connected).

Full notes: see CHANGELOG.md.

v0.5.1

Choose a tag to compare

@physwkim physwkim released this 07 Jul 07:27
40ff07f

[0.5.1] - 2026-07-07

A feature-bearing patch over the 0.5.0 crate rename: a new volume-rendering
widget, a toolbar restructure across the image-widget family, and the profile
overlay drawn on the image. Also the first cross-platform CI (Linux / macOS /
Windows), which surfaced and fixed a Direct3D-12 shader-portability bug. rsdm
and adl2rsdm are re-released in lockstep (no functional change beyond an
internal lint cleanup).

Added — rsplot

  • VolumeRaycaster — an interactive GPU direct-volume-rendering widget.
    Ray-marches a (depth, height, width) RGBA8 volume in a fragment shader
    (shaders/volume_raycaster.wgsl): a full-screen triangle un-projects each
    pixel through the inverse camera matrix, slab-tests the volume box, and
    front-to-back alpha-composites samples of a 3-D texture straight into egui's
    render pass (premultiplied-alpha blend, no offscreen target or depth buffer).
    Orbit / pan / wheel-zoom reuse the existing Camera + OrbitDrag / PanDrag
    interaction state machines. Transfer knobs: set_alpha_scale (global opacity),
    set_steps (samples per ray), set_cull_floor (skip near-transparent
    samples). Unlike ScalarFieldView (iso-surface geometry), this renders the
    volume itself — every voxel's colour and opacity contribute along the ray.
    Naga-validated headlessly plus an egui_kittest wgpu render test (opaque
    volume ⇒ visible colour, transparent volume ⇒ nothing).
  • Compact plot control toolbar. The Plot2D control toolbar now renders
    compact by default — the essential buttons (reset zoom, box-zoom / pan modes,
    invert Y, keep-aspect) stay in the row and everything else folds into a single
    ⋯ overflow menu, so the toolbar stays one line and the plot keeps its
    height. Toggle with Plot::set_toolbar_compact (toolbar_compact to read).
  • Shared control toolbar on ImageView, ImageStack, and CompareImages.
    All three now surface the standard silx plot controls above their own rows,
    matching the rest of the image-widget family (previously reachable only on the
    bare inner plot).
  • ImageView image controls as detached settings windows. The image-specific
    controls (interp / agg / alpha / profile / mask) are now toolbar buttons that
    each open their own detached settings window, matching silx's toolbar-action /
    dock-widget layout instead of inline combos and sliders. The colorbar stays a
    plain toggle.
  • Profile overlay drawn on the image. The profile ROI and its width band are
    now drawn directly on the image while a profile tool is active.

Fixed — rsplot

  • Curve rendering no longer fails on Windows / Direct3D-12. The curve
    fragment shader ended in a function-terminal discard, which naga's HLSL
    backend emits as a path with no return value; the D3D12 FXC compiler rejects
    it (X3507, "not all control paths return a value"), so create_render_pipeline
    for the curve pipeline — and every curve / scatter draw — failed on Windows.
    Vulkan and Metal tolerate it, so it was invisible until cross-platform CI. A
    trailing unreachable return keeps the shader portable.
  • The plot no longer zooms while a profile drag positions the ROI. A drag
    that places or resizes a profile ROI is consumed by the profile tool instead
    of also driving a box zoom.

v0.5.0 — crate rename (rsplot / rsdm / adl2rsdm)

Choose a tag to compare

@physwkim physwkim released this 05 Jul 16:38

Crate rename

The three crates have been renamed and republished on crates.io at 0.5.0:

old new
siplot rsplot — silx-style scientific plotting for egui/wgpu
sidm rsdm — PyDM-style EPICS display widgets + data engine on rsplot
adl2sidm adl2rsdm — MEDM .adl → RsDM Rust-source converter

The old crates (siplot 0.4.3, sidm 0.4.3, adl2sidm 0.4.3) are deprecated:
each publishes a final version that re-exports the renamed crate (pub use rsplot::* /
rsdm::* / adl2rsdm::*) plus a rename notice, so existing use siplot::… code keeps
compiling. Please switch your dependency:

rsplot = "0.5"
rsdm = "0.5"

The GitHub repository was renamed physwkim/siplot → physwkim/rsplot (the old URL redirects).

Also since 0.4.2

This release also carries the completed R2 (69/69) and R3 (24/24) reference→port
parity fix rounds against silx / PyDM / MEDM, including the R3 deferred batch: calc://
array binding, external MEDM colormap parsing, loc:// config-bearing gating, and
per-pen strip-chart normalization.

v0.4.2

Choose a tag to compare

@physwkim physwkim released this 02 Jul 03:25

Follow-up patch to the 0.4.1 trackpad-momentum fix. siplot, sidm, and adl2sidm published to crates.io in lockstep; only siplot has a functional change.

Added — siplot

Plot::scroll_zoom flag (default true) to disable wheel/trackpad zoom per plot. When false the whole wheel handler is skipped — no scroll is read, no momentum guard runs, no zoom happens — while box-drag zoom, the toolbar Home/Zoom buttons, and the context menu are unaffected. Opt out with plot.plot_mut().scroll_zoom = false.

Fixed — siplot

  • Wheel-zoom no longer stops working on plots that refit to data on every update. The 0.4.1 momentum guard was armed inside the low-level reset_zoom_to_data_range, which the widget also funnels its autoscale-refit-on-content-change through (apply_auto_limits → apply_limits_from_data_bounds). A plot rebuilding its curves each update re-armed the guard every frame, so the handler swallowed every scroll and wheel-zoom stopped entirely (box-drag zoom and Zoom Back were unaffected). The arm now lives on the user-facing reset verbs (Plot::reset_zoom arms after the refit; Plot::zoom_back already armed its own) and the shared low-level path no longer arms.
  • Context-menu "Reset Zoom" refits to the live data range instead of restoring a stale home snapshot. For a long-lived plot whose content is rebuilt while zoomed in, home_limits was re-captured as the zoomed view, so Reset Zoom reverted to the zoom (a no-op). It now calls Plot::reset_zoom — refit the autoscale-on axes to live data, matching the toolbar Home button and silx resetZoom.

Full changelog: v0.4.1...v0.4.2