Line Plot Widget Roadmap
LinePlotWidget Development Roadmap
This document tracks remaining development tasks for the Line Plot Widget. For documentation of completed features and current architecture, see the main developer documentation.
Completed Work Summary
The following features are complete and documented in index.qmd:
- Widget skeleton: EditorState pattern, registration, state/view/properties factories
- State management:
LinePlotStatewithPlotAlignmentState,RelativeTimeAxisState,VerticalAxisStatecomposition; separateLinePlotStateData.hppfor fuzz-testable serializable struct - Rendering pipeline:
GatherResult<AnalogTimeSeries>integration,LineBatchDataconstruction,BatchLineRendererGPU rendering, auto-fit Y/X bounds - Per-series options: Line style (color, thickness, alpha) via
LineStyleData/LineStyleState/LineStyleControls— same infrastructure as OnionSkinViewWidget and TemporalProjectionViewWidget - Axis system:
RelativeTimeAxisWidget(X) andVerticalAxisWidget(Y) with bidirectional sync - Interaction: Separated X/Y zoom, pan, double-click time navigation via shared
PlotInteractionHelpers - Line selection: Ctrl+Click drag intersection stroke (add/remove modes), GPU compute shader and CPU intersector backends, shared
LineSelectionHelperswith TemporalProjectionViewWidget - Properties panel: Alignment controls (
PlotAlignmentWidget), series management (add/remove/table), line options (thickness, color), axis range controls in collapsible sections, selection instructions, color-by-group checkbox - State serialization: Full round-trip JSON via
rfl::json - DataManager observer: Combo box population and cleanup in destructor
- Group integration:
GroupManagerpassed at construction, right-click context menu via sharedGroupContextMenuHandler, trial→EntityID mapping via cached alignment times, per-line group color overrides inBatchLineRenderershader, generation-counter-based reactive updates,color_by_groupstate toggle
Not yet implemented: Mean/confidence interval overlay, external feature coloring, distribution tooltips, SVG export, and cross-widget linking described below.
Phase 2: Mean / Summary Overlay with Confidence Intervals
Status: Not started. Depends on the existing GatherResult and batch rendering pipeline.
Goal: Optionally display a summary line (mean, median) of the overlaid trial lines, with optional confidence intervals, similar to the summary statistics shown in a PSTH.
2.1 Summary Mode Enum
enum class LinePlotSummaryMode {
None, ///< No summary overlay (default)
Mean, ///< Arithmetic mean across trials
Median ///< Median across trials
};2.2 Confidence Interval Options
enum class ConfidenceIntervalType {
None, ///< No confidence band
SEM, ///< Standard error of the mean
StdDev, ///< Standard deviation
CI95, ///< 95% confidence interval
Percentile ///< User-defined percentile band (e.g., 25th–75th)
};
struct LinePlotSummaryConfig {
LinePlotSummaryMode mode = LinePlotSummaryMode::None;
ConfidenceIntervalType ci_type = ConfidenceIntervalType::None;
double ci_alpha = 0.2; ///< Opacity of confidence band fill
std::string summary_color = "#FF0000"; ///< Color of the summary line
double summary_thickness = 2.5; ///< Thickness of the summary line
// For Percentile mode
double lower_percentile = 25.0;
double upper_percentile = 75.0;
};2.3 State Extension
struct LinePlotStateData {
// ... existing fields ...
LinePlotSummaryConfig summary;
};2.4 Implementation
After building LineBatchData from trial data, compute the summary:
- Resample all trial lines to a common time grid (the
GatherResultmay have different sample counts per trial due to varying TimeFrame resolutions) - Pointwise aggregation: For each time sample, compute the summary statistic (mean or median) across all trials
- Confidence interval: Compute upper/lower bounds at each sample point
- Render summary line as a thicker line on top of the trial lines via
SceneRendereror as an additional entry in theLineBatchData - Render confidence band as a filled semi-transparent polygon (similar to the confidence band rendering proposed in the PSTH Roadmap Phase 3)
2.6 Properties Panel
Add to LinePlotPropertiesWidget in a “Summary” collapsible section:
- Summary mode combo (None, Mean, Median)
- Confidence interval type combo (None, SEM, StdDev, CI95, Percentile)
- Summary line color picker
- Summary line thickness spinbox
- CI opacity slider
- Percentile range spinboxes (visible only for Percentile mode)
Phase 3: External Feature Coloring
Status: Not started. Design influenced by EventPlot Roadmap Phase 5.
Goal: Allow trial lines to be colored by an external feature stored in a TensorData column, enabling visual grouping by categorical or continuous features.
Concept
The same TensorData infrastructure used for EventPlotWidget external feature sorting/coloring applies here. A TensorData with interval-based rows can provide per-trial feature values:
- Binary features (0.0/1.0 from threshold transforms) → two-color categorical coloring
- Continuous features (e.g., firing rate, latency) → colormap-based gradient coloring
- Alpha scaling — instead of (or in addition to) color, the feature value scales line opacity
3.1 Color Configuration
struct LineColorConfig {
std::string color_mode = "fixed"; // "fixed", "by_feature", or "by_group"
// For fixed coloring (existing behavior) — uses LinePlotOptions::hex_color
// For feature-based coloring
ExternalFeatureConfig feature; // Which TensorData column
std::string colormap = "viridis"; // Colormap name
double color_min = 0.0;
double color_max = 1.0;
bool use_auto_range = true;
// Alpha scaling (independent of color mode)
bool alpha_by_feature = false;
ExternalFeatureConfig alpha_feature;
double alpha_min = 0.1;
double alpha_max = 1.0;
};The ExternalFeatureConfig struct is shared with EventPlotWidget:
struct ExternalFeatureConfig {
std::string tensor_key; // DataManager key for TensorData
std::string column_name; // Column within the TensorData
bool ascending = true; // Sort direction (for sorting use)
};3.2 Colormap Infrastructure
This feature depends on the colormap infrastructure proposed in the HeatmapWidget Roadmap Phase 2:
namespace CorePlotting::Colormaps {
using ColormapFunction = std::function<glm::vec4(float t)>;
ColormapFunction getColormap(ColormapPreset preset);
glm::vec4 mapValue(ColormapFunction const & cmap, float value, float vmin, float vmax);
}The colormap system in CorePlotting/Colormaps/ is shared across:
| Widget | Usage |
|---|---|
| LinePlotWidget | Per-trial line color from feature value |
| EventPlotWidget | Per-trial event color (EventPlot Roadmap Phase 5) |
| HeatmapWidget | Heatmap cell color (Heatmap Roadmap Phase 2) |
| SpectrogramWidget | Spectrogram cell color |
3.3 Per-Line Color in BatchLineRenderer
The current BatchLineRenderer uses global colors for normal/selected/hover states. To support per-line coloring, the renderer needs to be extended:
/// Per-line color override (optional — falls back to global if empty)
std::vector<glm::vec4> per_line_colors;When per_line_colors is non-empty, each line uses its own color instead of the global normal color. Selected and hover states could blend with the per-line color.
This renderer extension also benefits TemporalProjectionViewWidget for entity-based coloring.
3.4 Properties Panel
Add to LinePlotPropertiesWidget in a “Trial Coloring” collapsible section:
- Color mode combo — Fixed / By Feature / By Group
- TensorData combo box — lists
TensorDatakeys from DataManager - Column combo box — shows columns of the selected
TensorData - Colormap combo — colormap preset selection
- Range controls — auto/manual min/max spinboxes
- Alpha scaling checkbox — enables alpha-by-feature mode
- Alpha feature controls — separate TensorData/column for alpha (if different from color)
Phase 4: Distribution Tooltips
Status: Not started. New shared infrastructure needed.
Goal: When the user hovers over the plot, display a tooltip containing a vertical histogram showing the distribution of Y values across trials at that time point. This helps assess the reliability of an “average” (or summary) value by showing trial-to-trial variability.
4.1 Distribution Computation
At a given relative time \(t\):
- For each trial, interpolate or look up the signal value at time \(t\)
- Collect the \(N\) values (one per trial) into a distribution
- Compute a histogram of these values with configurable bin count
4.2 Tooltip Image Generation
Generate a small image (e.g., 150×200 pixels) showing:
- A vertical histogram (bins on Y-axis matching the plot’s value axis, count/frequency on X-axis)
- Optional summary statistics overlay (mean, median, std dev)
- Current time position label
The image should be rendered off-screen (using QImage / QPainter) and displayed in a Qt tooltip.
4.3 Integration with PlotTooltipManager
The tooltip lifecycle (dwell timer, suppression during pan/zoom, show/hide) is handled by the shared PlotTooltipManager (Plots/Common/TooltipManager/). The LinePlotWidget provides two callbacks:
- Hit test provider: Converts the screen position to a world X coordinate (relative time). Always succeeds when the cursor is within the plot area, since distributions can be computed at any time point.
- Content provider: Receives the hit, computes the cross-trial distribution at that time, renders a vertical histogram into a
QPixmapviaQPainter, and returns it asPlotTooltipContent. ThePlotTooltipManagerdisplays the pixmap in its built-inPixmapPopupwidget.
_tooltip_mgr = std::make_unique<PlotTooltipManager>(this, 100); // 100ms dwell for responsiveness
_tooltip_mgr->setHitTestProvider([this](QPoint pos) -> std::optional<PlotTooltipHit> {
QPointF world = screenToWorld(pos);
PlotTooltipHit hit;
hit.world_x = static_cast<float>(world.x());
hit.world_y = static_cast<float>(world.y());
return hit;
});
_tooltip_mgr->setContentProvider([this](PlotTooltipHit const & hit) -> PlotTooltipContent {
PlotTooltipContent content;
content.pixmap = renderDistributionHistogram(hit.world_x);
return content;
});The HeatmapWidget Roadmap Phase 8 uses the same PlotTooltipManager with a pixmap content provider for its miniature PSTH + raster tooltips.
| Widget | Tooltip Content | Content Type |
|---|---|---|
| EventPlotWidget | Trial index + time | Text (implemented) |
| LinePlotWidget | Vertical histogram of Y values at hovered time point | Pixmap |
| HeatmapWidget | Miniature PSTH + raster for hovered unit | Pixmap |
| ScatterPlotWidget | Point coordinates + TimeFrameIndex | Text |
4.4 Hover Detection
Hover detection is handled by PlotTooltipManager::onMouseMove(), called from LinePlotOpenGLWidget::mouseMoveEvent(). The manager’s dwell timer prevents expensive distribution computations during rapid mouse movement. Tooltips are automatically suppressed during panning via the is_interacting parameter.
4.5 Configuration
Add to LinePlotStateData:
struct TooltipConfig {
bool enabled = false;
int histogram_bins = 20;
bool show_summary_stats = true; // Mean, median overlay
};Phase 5: SVG Export
Status: Not started. New shared infrastructure needed.
Goal: Allow the user to save the current plot view as an SVG file for publication-quality figures.
5.1 SVG Generation
SVG export should produce a vector graphics representation of the current view:
- All trial lines (with current colors, thicknesses, and opacity)
- Selected lines highlighted
- Summary line and confidence interval (if enabled)
- Axis labels and tick marks
- Background
5.2 Implementation Approaches
Option A: QPainter-based SVG (recommended initial approach)
Use QSvgGenerator with QPainter to re-render the scene:
void LinePlotWidget::exportToSvg(QString const & file_path) {
QSvgGenerator generator;
generator.setFileName(file_path);
generator.setSize(size());
generator.setViewBox(QRect(0, 0, width(), height()));
QPainter painter(&generator);
// Re-render all lines via QPainter rather than OpenGL
renderToQPainter(painter);
painter.end();
}This requires a separate renderToQPainter() code path that iterates over the LineBatchData and draws lines using QPainter::drawPolyline().
Option B: Direct SVG string generation
Build the SVG XML directly from LineBatchData, which gives more control over SVG structure and styling but requires manual coordinate transformation.
5.4 UI
Add to LinePlotPropertiesWidget or toolbar:
- “Export SVG” button — opens a file save dialog, then calls the export function
- Optional: “Copy to Clipboard” — renders as SVG/PNG and copies to clipboard
Phase 6: Cross-Widget Linking
Status: Design phase. Foundation exists via EditorRegistry::setCurrentTime() for double-click navigation.
Goal: Enable LinePlotWidget to participate in the SelectionContext system for inter-widget communication.
6.1 SelectionContext Integration
Following the pattern described in the EventPlot Roadmap Phase 6:
- As receiver: When a
DigitalEventSeriesis selected in another widget, the LinePlotWidget could automatically update its alignment event key (unless pinned) - As source: When trials are selected, emit the selected alignment event EntityIDs through
SelectionContextfor other widgets to react
6.2 Pinning
Add a pinned flag to LinePlotStateData:
struct LinePlotStateData {
// ... existing fields ...
bool pinned = false;
};When pinned, the widget ignores external SelectionContext changes. This enables workflows where one Line Plot is locked to a specific signal while another follows the user’s selection.
6.3 Companion Widget Workflow
The Line Plot Widget is the analog-signal companion to the discrete-event EventPlotWidget and PSTHWidget. A typical linked workflow:
User selects alignment event in any trial-aligned widget
↓
SelectionContext::setSelectedData(alignment_event_key)
↓
LinePlotWidget (unpinned) updates alignment event
↓
EventPlotWidget (unpinned) updates alignment event
↓
PSTHWidget (unpinned) updates alignment event
↓
All three widgets show data aligned to the same event
Phase 7: Multi-Series Rendering
Status: Partially implemented. The state supports multiple series (std::map<std::string, LinePlotOptions>) with per-series LineStyleData (color, thickness, alpha) and LineStyleState instances. The properties panel uses the shared LineStyleControls widget to edit the selected series’ style. However, the renderer currently only uses the first series key for data gathering.
Goal: Render multiple AnalogTimeSeries overlaid in the same plot, each with independent appearance.
7.1 Multi-Series Data Gathering
Extend rebuildScene() to iterate over all series:
for (auto const & [name, options] : _state->data().plot_series) {
auto gathered = createAlignedGatherResult<AnalogTimeSeries>(
_data_manager, options.series_key, alignment_data);
// Build batch for this series with per-series color
auto batch = buildLineBatchFromGatherResult(gathered, alignment_times);
// Upload and render with series-specific color from options.hex_color
}7.2 Per-Series Color in BatchLineRenderer
The BatchLineRenderer now supports per-line color overrides via a vec4 vertex attribute (implemented in Phase 1 for group coloring). Multi-series rendering can reuse this infrastructure by assigning each series’ color as the per-line override for its lines.
7.3 Legend
When multiple series are displayed, a legend should map series names to their colors. This could be rendered as:
- An OpenGL overlay in the top-right corner of the plot
- A separate legend widget in the properties panel
Dependency Graph
Phase 6: Cross-Widget Linking (uses groups for broadcast)
Phase 2: Mean / CI Overlay
Phase 3: External Feature Coloring
├── depends on: CorePlotting::Colormaps (shared w/ HeatmapWidget, EventPlotWidget)
└── Per-line color in BatchLineRenderer (done in Phase 1)
Phase 4: Distribution Tooltips
└── depends on: shared PlotTooltipRenderer (also needed by HeatmapWidget)
Phase 5: SVG Export
└── depends on: shared Export infrastructure
Phase 7: Multi-Series Rendering
└── Per-line color in BatchLineRenderer (done in Phase 1)
Phase 1 is complete. Phases 2, 3, and 5 are independent and can proceed in parallel. Phase 3 no longer blocked (per-line color infrastructure done in Phase 1). Phase 4 introduces new shared infrastructure.