Skip to content

ScatterWidget: add a DataFcn callback binding for computed XY clouds #265

Description

@HanSur94

Problem / motivation

ScatterWidget can only plot two registered sensors against each other. Its public
config is just SensorX / SensorY / SensorColor / MarkerSize / Colormap
(ScatterWidget.m:2-8), and refresh reads strictly from obj.SensorX.getXY() /
obj.SensorY.getXY() (ScatterWidget.m:46-54).

This is a binding-model asymmetry: the two other chart-shaped data widgets both accept a
DataFcn callback as an alternative to a Tag binding —

  • BarChartWidget.DataFcn (BarChartWidget.m:3), consumed in refresh
    (elseif ~isempty(obj.DataFcn) ... result = obj.DataFcn();, :51-52)
  • HistogramWidget.DataFcn (HistogramWidget.m:3), consumed in refresh
    (elseif ~isempty(obj.DataFcn) ... data = obj.DataFcn();, :52-53), and round-tripped
    as source.type='callback' (:126-128, :146-149)

ScatterWidget is the only one of the three with no callback path
(grep -niE "DataFcn|callback|feval" ScatterWidget.m → 0). So to scatter two computed/
transformed
quantities, an analyst must first register throwaway tags just to plot them.

Proposed feature

Add an optional DataFcn callback to ScatterWidget, mirroring its sibling widgets, that
returns the XY (and optional color) data directly:

w = ScatterWidget('Title', 'Lag plot');
w.DataFcn = @() struct('x', x(1:end-1), 'y', x(2:end));   % x(t) vs x(t+1)
% optional color channel:
w.DataFcn = @() struct('x', a, 'y', b, 'c', residual);

Use cases: residual plots, lag / return-map plots, correlation of two on-the-fly computed
channels, or a pre-downsampled point cloud — none of which need to exist as registered Tags.

Rough sketch

  • Lib/class: libs/Dashboard/ScatterWidget.m (single file).
  • Public API: add DataFcn = [] (default []). Convention matches the siblings:
    @() struct('x', xvec, 'y', yvec) with an optional .c color vector.
  • Render: in refresh, after the SensorX/SensorY branch, add
    elseif ~isempty(obj.DataFcn) that calls r = obj.DataFcn() and reads r.x/r.y
    (and r.c if present), feeding the existing rebuild block (the same
    scatter(...) / line(...) path already used for the colored/uncolored cases).
    Sensor binding keeps priority when both are set (matches BarChart/Histogram precedence).
  • Serialize: mirror Histogram/BarChart — emit
    s.source = struct('type','callback','function', func2str(obj.DataFcn)) in toStruct
    when DataFcn is set and no sensors are wired; restore via str2func in fromStruct.

Value

Medium-High. Removes the "register a throwaway tag just to scatter it" friction for
exploratory XY analysis, exactly as DataFcn already does for the bar/histogram tiles.

Constraints check

  • Toolbox-free: yes — anonymous handle + feval, existing scatter/line calls.
  • Backward-compatible: yes — default DataFcn=[] is byte-identical to today; old
    scatter saves have no source key, load with DataFcn=[], and render exactly as before.
  • Pure MATLAB/Octave: yes. Works through the existing DashboardWidget contract — no
    base-class or layout change.

Effort estimate

S–M — one public prop + one elseif branch in refresh (reusing the existing rebuild
block) + a toStruct/fromStruct round-trip. Plus one test: a DataFcn returning
struct('x',1:10,'y',(1:10).^2) renders a 10-point cloud with no sensors wired, and the
handle round-trips through toStruct/fromStruct.


AI-proposed via /feature-scout — needs a human product decision before implementation.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions