Problem / motivation
A gauge is the canonical "actual vs. where it should be" widget — it most often
visualizes a controlled quantity: a pressure setpoint, a target RPM, a nominal
temperature, a commanded position. The single most common question an engineer asks a
gauge is "am I at target?"
Today GaugeWidget can show only the current value (a colored arc + needle, across the
arc / donut / bar / thermometer styles) and exposes no way to mark a
target/setpoint/nominal reference. Verified the public properties are exactly
ValueFcn / Range / Units / StaticValue / Style / Threshold (GaugeWidget.m:9-16);
grep -nE "Target|Setpoint|Marker" over the file returns nothing. The dial therefore
shows a value with nothing to read it against.
This is distinct from the already-filed #235 (ShowZones), which draws threshold
bands (acceptable ranges), and from #232 (Format) and #229 (Reduce). A single explicit
target pip — the bright reference mark on a real pressure gauge — is a different, core
affordance.
Proposed feature
Add an opt-in Target reference marker to GaugeWidget: a static tick drawn on the
gauge at a caller-supplied value, so current-vs-target reads at a glance.
w = GaugeWidget('Sensor', pressureTag, 'Range', [0 100], ...
'Units', 'bar', 'Target', 75); % draws a reference pip at 75
Rough sketch
- Lib / class:
libs/Dashboard/GaugeWidget.m (single file).
- Public API:
Target = [] — numeric scalar in Range units. Default [] → no marker
(byte-identical to today). Optionally accept a 1xN vector for multiple pips
(low/high commanded band) — scalar is the MVP.
TargetColor = [] — default → theme accent / sensible contrast color.
- Render: in each
render* style, after the background arc/track is drawn, map
Target through the same value→fraction→angle mapping the needle / foreground arc
already use (renderArc 240-deg sweep, 210-deg→-30-deg, GaugeWidget.m:341-352;
analogous linear maps in renderBar / renderThermometer / renderDonut) and draw one
short radial tick:
line(obj.hAxes, [rInner rOuter]*cos(thetaTarget), [rInner rOuter]*sin(thetaTarget), 'Color', targetColor, 'LineWidth', 2)
for the radial styles; a straight tick for bar / thermometer. Clamp to Range; skip
cleanly when Target is [], NaN, or Range is empty/degenerate.
- The marker is static (set once at render), so it adds zero per-tick cost — it
does not touch the update* fast path and cannot degrade live refresh.
- Serialize: emit
s.target / s.targetColor in toStruct (GaugeWidget.m:176-193)
only when non-default; read with isfield guards in fromStruct (:195-) defaulting to
[] — exactly the range/units/style round-trip pattern already in the file. Old
saves load with no marker and render unchanged.
- Test:
Target=75 with Range=[0 100] places the tick at the 75% sweep angle;
Target=[] renders byte-identically to today; the value round-trips through serialization.
Value
High. The setpoint-vs-actual read is arguably the reason an engineer reaches for a gauge
rather than a NumberWidget. The marker closes that core gap with a familiar affordance and
composes cleanly with #235 (ShowZones bands = acceptable ranges) and #232 (Format).
Constraints check
- Toolbox-free: ✅ only base
line / cos / sin / fill.
- Backward-compatible: ✅ default
Target = [] → no marker, identical render; serialized
dashboards untouched (non-default-only emit + isfield guards on load).
- Pure MATLAB/Octave: ✅ no toolbox calls; works through the existing
DashboardWidget
contract (no base-class or layout change).
- Performance: ✅ static marker drawn once at render; does not touch the live
update*
path → no refresh-rate impact.
Effort estimate
S — single file: two public props + a small drawTarget_ helper called from each
render* style + a toStruct/fromStruct round-trip + one test.
AI-proposed via /feature-scout — needs a human product decision before implementation.
Problem / motivation
A gauge is the canonical "actual vs. where it should be" widget — it most often
visualizes a controlled quantity: a pressure setpoint, a target RPM, a nominal
temperature, a commanded position. The single most common question an engineer asks a
gauge is "am I at target?"
Today
GaugeWidgetcan show only the current value (a colored arc + needle, across thearc / donut / bar / thermometerstyles) and exposes no way to mark atarget/setpoint/nominal reference. Verified the public properties are exactly
ValueFcn / Range / Units / StaticValue / Style / Threshold(GaugeWidget.m:9-16);grep -nE "Target|Setpoint|Marker"over the file returns nothing. The dial thereforeshows a value with nothing to read it against.
This is distinct from the already-filed #235 (ShowZones), which draws threshold
bands (acceptable ranges), and from #232 (Format) and #229 (Reduce). A single explicit
target pip — the bright reference mark on a real pressure gauge — is a different, core
affordance.
Proposed feature
Add an opt-in
Targetreference marker toGaugeWidget: a static tick drawn on thegauge at a caller-supplied value, so current-vs-target reads at a glance.
Rough sketch
libs/Dashboard/GaugeWidget.m(single file).Target = []— numeric scalar inRangeunits. Default[]→ no marker(byte-identical to today). Optionally accept a 1xN vector for multiple pips
(low/high commanded band) — scalar is the MVP.
TargetColor = []— default → theme accent / sensible contrast color.render*style, after the background arc/track is drawn, mapTargetthrough the same value→fraction→angle mapping the needle / foreground arcalready use (
renderArc240-deg sweep, 210-deg→-30-deg,GaugeWidget.m:341-352;analogous linear maps in
renderBar/renderThermometer/renderDonut) and draw oneshort radial tick:
line(obj.hAxes, [rInner rOuter]*cos(thetaTarget), [rInner rOuter]*sin(thetaTarget), 'Color', targetColor, 'LineWidth', 2)for the radial styles; a straight tick for
bar/thermometer. Clamp toRange; skipcleanly when
Targetis[],NaN, orRangeis empty/degenerate.does not touch the
update*fast path and cannot degrade live refresh.s.target/s.targetColorintoStruct(GaugeWidget.m:176-193)only when non-default; read with
isfieldguards infromStruct(:195-) defaulting to[]— exactly therange/units/styleround-trip pattern already in the file. Oldsaves load with no marker and render unchanged.
Target=75withRange=[0 100]places the tick at the 75% sweep angle;Target=[]renders byte-identically to today; the value round-trips through serialization.Value
High. The setpoint-vs-actual read is arguably the reason an engineer reaches for a gauge
rather than a
NumberWidget. The marker closes that core gap with a familiar affordance andcomposes cleanly with #235 (ShowZones bands = acceptable ranges) and #232 (Format).
Constraints check
line/cos/sin/fill.Target = []→ no marker, identical render; serializeddashboards untouched (non-default-only emit +
isfieldguards on load).DashboardWidgetcontract (no base-class or layout change).
update*path → no refresh-rate impact.
Effort estimate
S — single file: two public props + a small
drawTarget_helper called from eachrender*style + atoStruct/fromStructround-trip + one test.AI-proposed via /feature-scout — needs a human product decision before implementation.