Scatter Plot Widget Roadmap

ScatterPlotWidget Development Roadmap

This document tracks remaining development tasks for the Scatter 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: ScatterPlotState with HorizontalAxisState, VerticalAxisState composition; ScatterPlotStateData serializable struct

  • Axis system: HorizontalAxisWidget (X) and VerticalAxisWidget (Y) with bidirectional sync, including the “silent update” pattern

  • Interaction: Separated X/Y zoom, pan via shared PlotInteractionHelpers

  • Properties panel: Axis range controls in collapsible Section widgets; data source selection UI (key, column, offset per axis); reference line toggle; compatibility status label

  • OpenGL lifecycle: ScatterPlotOpenGLWidget initialization, projection/view matrix management, pan/zoom mouse handlers

  • State serialization: Full round-trip JSON via rfl::json

  • Registration wiring: timePositionSelectedEditorRegistry::setCurrentTime() connection

  • Data source descriptor: ScatterAxisSource struct defining data key, tensor column, and temporal offset

  • Row type compatibility validation: resolveSourceRowType() and checkSourceCompatibility() functions in Core/SourceCompatibility.hpp/.cpp with full test coverage (21 test cases, 52 assertions)

  • Data source UI: Key/column/offset combo boxes per axis with DataManager observer, compatibility label displaying checkSourceCompatibility() results

  • Point pair computation: buildScatterPoints() factory function in Core/BuildScatterPoints.hpp/.cpp supporting all compatible source type combinations (ATS×ATS, ATS×TensorTFI, TensorTFI×ATS, TensorTFI×TensorTFI, Ordinal×Ordinal, Interval×Interval) with temporal offset and TimeFrameIndex join. Tested in BuildScatterPoints.test.cpp.

  • Point rendering: SceneBuilder→SceneRenderer pipeline using addGlyphs() for points with single instanced draw call

  • Auto-fit bounds: Automatic X/Y bounds detection from data with 5% padding on first data load (when bounds are at defaults)

  • Reference line rendering: \(y = x\) diagonal via addPolyLine() with show/hide toggle in properties panel; endpoints update on pan/zoom

  • DataManager observer: Observer registration/removal in properties widget for combo box synchronization; stale key handling clears axis source when selected data key is deleted from DataManager; tested in ScatterPlotPropertiesWidget.test.cpp

  • Double-click-to-navigate: Point hit testing via shared SceneHitTester + QuadTree<EntityId> spatial index, pointDoubleClickedtimePositionSelectedEditorRegistry::setCurrentTime() signal chain, yellow highlight glyph for navigated-to point

  • Glyph customization: GlyphStyleData in ScatterPlotStateData with default blue circle style (#3388FF, size 5, alpha 0.8); GlyphStyleState owned by ScatterPlotState with glyphStyleChanged signal; GlyphStyleControls embedded in properties panel “Glyph Options” collapsible section; rendering uses toRenderableGlyphType() and hexColorToVec4() from GlyphStyleConversion.hpp; full round-trip JSON serialization

  • Point selection: Dual selection modes (SinglePoint/Polygon) via combo box in properties panel; SinglePoint mode: Ctrl+Click toggles selection, Shift+Click removes; Polygon mode: Ctrl+Click adds vertices, Enter closes polygon, Backspace undoes, Escape cancels; selectPointsInPolygon() utility in CorePlotting/Selection/; selected points rendered with distinct style (orange, +4 size); integration with GroupContextMenuHandler for right-click group operations; fully tested

  • Temporal offset for outlier detection: Per-axis time_offset field in ScatterAxisSource enables lag-plots (\(x(t)\) vs \(x(t-1)\)); buildScatterPoints() applies offsets for all source type combinations; offset spinboxes (-10000 to 10000) in properties panel per axis; points where offset pushes index outside valid range are excluded; tested in BuildScatterPoints.test.cpp

  • Hover tooltip: PlotTooltipManager integration showing X/Y values and TimeFrameIndex on dwell; pan/zoom suppression; text provider with 3-decimal precision

Not yet implemented: feature coloring (Phase 7), stale key handling (Phase 6.3).


Phase 7: Feature Coloring via TensorData

Status: Design phase. Depends on point rendering and the shared colormap infrastructure.

Goal: Color scatter points by an external feature value from a TensorData column, enabling visual grouping by continuous or categorical features.

7.1 Color Configuration

struct ScatterColorConfig {
    std::string color_mode = "fixed";    ///< "fixed" or "by_feature"

    // For feature-based coloring
    std::string tensor_key;              ///< DataManager key for the color TensorData
    std::string column_name;             ///< Column within the TensorData
    std::string colormap = "viridis";    ///< Colormap name
    double color_min = 0.0;
    double color_max = 1.0;
    bool use_auto_range = true;
};

7.2 Shared Colormap Infrastructure

This depends on the colormap infrastructure proposed in the HeatmapWidget Roadmap and referenced across multiple widget roadmaps:

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
ScatterPlotWidget Per-point color from feature value
LinePlotWidget Per-trial line color from feature value
EventPlotWidget Per-trial event color
HeatmapWidget Heatmap cell color

7.3 Properties Panel

Add to ScatterPlotPropertiesWidget in a “Point Coloring” collapsible section:

  1. Color mode combo — Fixed / By Feature
  2. TensorData combo box — lists TensorData keys from DataManager
  3. Column combo box — shows columns of the selected TensorData
  4. Colormap combo — colormap preset selection
  5. Range controls — auto/manual min/max spinboxes

Shared Components Summary

Components that have been factored into shared locations or are candidates for sharing:

Component Status Location Used By
selectPointsInPolygon Implemented CorePlotting/Selection/ ScatterPlotWidget; available for TemporalProjectionViewWidget
SelectionInstructions Implemented Plots/Common/SelectionInstructions.hpp ScatterPlotWidget, TemporalProjectionViewWidget
GroupManagementPanel ⏸️ Deferred N/A ScatterPlotWidget uses existing GroupContextMenuHandler via right-click; dedicated panel not needed yet
Colormaps Planned CorePlotting/Colormaps/ ScatterPlotWidget (Phase 7), HeatmapWidget, EventPlotWidget, LinePlotWidget

Phase 8: Hover Tooltip

Status: ✅ Implemented. See index.qmd § Hover Tooltip for documentation.


Design Decisions

  1. Point count scalability: The SceneBuilder::addGlyphs()GlyphRenderer pipeline uses instanced rendering (single GPU draw call for all points in a batch). This is the same path EventPlotWidget uses for large spike counts and scales well to tens of thousands of points. No dedicated PointBatchRenderer is needed.

  2. Polygon interaction: Uses the existing PolygonInteractionController from CorePlotting/Interaction/. This shared controller handles click-based polygon creation, vertex drag modification, cursor preview, and auto-close detection.

  3. Reference line: Only \(y = x\) reference line for now. Can be generalized to arbitrary linear reference lines (\(y = ax + b\)) in a future phase if needed.

  4. Hit testing via shared QuadTree: Point hit testing uses the existing SceneHitTester (CorePlotting/Interaction/SceneHitTester.hpp) which queries the QuadTree<EntityId> spatial index built automatically by SceneBuilder::addGlyphs(). This is the same infrastructure used by EventPlotWidget for double-click navigation and click-to-select. The QuadTree provides O(log n) nearest-neighbor lookup, scaling well to large point counts without widget-specific spatial indexing code. Navigation uses mouseDoubleClickEvent, matching EventPlotWidget. Hover tooltips (Phase 8) use the shared PlotTooltipManager for dwell timer and lifecycle management.

  5. Two-batch rendering for selection/highlight: GlyphRenderer renders each RenderableGlyphBatch in order, with its own u_point_size / u_glyph_size uniform. Per-instance color is already supported by the shader (a_instance_color / a_color VBO attribute), but per-instance size is not — size is a per-batch uniform. Therefore, rendering selected/highlighted points at a different size requires a second addGlyphs() call. This results in exactly two GPU draw calls (one for unselected, one for selected) regardless of the selection count, with no shader changes needed.

  6. Single dataset per plot: Each ScatterPlotWidget displays a single X/Y data source pair. Users can open multiple scatter plot instances via the Analysis Dashboard for side-by-side comparison of different variable pairs.

See Also