Temporal Projection View Widget Roadmap

TemporalProjectionViewWidget Development Roadmap

This document tracks remaining development tasks for the Temporal Projection View 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: TemporalProjectionViewState with HorizontalAxisState, VerticalAxisState composition; TemporalProjectionViewStateData serializable struct with rfl::json

  • Dual rendering pipeline: SceneRenderer for point glyphs, BatchLineRenderer for selectable lines

  • Data key management: Add/remove multiple PointData and LineData keys via properties panel

  • Spatial mapping: SpatialMapper::mapAllPoints() for flattening all time frames; buildLineBatchFromLineData() for line segments

  • Auto-fit bounds: Bounding box computation from actual data with 2% margin, zoom/pan reset on bounds change

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

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

  • Point selection: Ctrl+Click toggle via SceneHitTester::queryQuadTree() with 10px tolerance

  • Line selection: Ctrl+Click drag intersection stroke via shared LineSelectionHelpers, GPU compute shader and CPU intersector backends

  • Selection state: std::unordered_set<EntityId> with preservation across scene rebuilds

  • Rendering controls: Point size and line width spinboxes in properties panel

  • Selection mode toggle: None / Point / Line combo box in properties panel

  • DataManager observer: Combo box population and cleanup in destructor

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

  • Registration wiring: timePositionSelectedEditorRegistry::setCurrentTime() connection

  • Line style customization: LineStyleData (color, thickness, alpha) via LineStyleState/LineStyleControls — global single style, replaces hardcoded red

  • Polygon selection: Dual-mode selection (Point/Line/Polygon) via combo box; Enter to close polygon, Backspace to undo vertex, Escape to cancel; uses shared PolygonInteractionController and selectPointsInPolygon() from CorePlotting/Selection/; polygon preview via PreviewRenderer; additive selection (adds to existing)

  • Group integration: Right-click context menu via GroupContextMenuHandler for “Add to Group”, “Create New Group”, “Remove from Group”; integrated with EntityRegistry; selection instructions via shared SelectionInstructions helpers

  • Group-based coloring: color_by_group toggle in properties panel; applyGroupColorsToScene() applies group colors to point glyphs with priority (selected → group → default); GroupManager signal connections trigger scene rebuild on group changes

Not yet implemented: double-click time navigation, subgroup visibility filtering, per-key rendering options described below.


Phase 3: Double-Click Time Navigation

Status: Not started. The mouseDoubleClickEvent handler exists but delegates to the base class. The timePositionSelected signal is already wired to EditorRegistry::setCurrentTime() via the registration.

Goal: Double-clicking on a point or line should navigate other time-synced widgets to the associated time position.

3.1 Point Double-Click → Time Navigation

When in point selection mode and the user double-clicks on a point glyph:

  1. Perform hit testing via SceneHitTester::queryQuadTree() (same as single-click)
  2. Look up the EntityId for the hit point
  3. Resolve the EntityIdTimeFrameIndex mapping from the PointData source
  4. Convert TimeFrameIndexTimePosition via the TimeFrame
  5. Emit timePositionSelected(position)EditorRegistry::setCurrentTime()

This is the same navigation pattern used by EventPlotWidget (eventDoubleClickedsetCurrentTime()).

3.2 Line Double-Click → Time Navigation

When in line selection mode and the user double-clicks near a line:

  1. Find the nearest line segment to the click position
  2. Look up the EntityId for that line
  3. Resolve the EntityIdTimeFrameIndex mapping from the LineData source
  4. Convert to TimePosition and emit timePositionSelected

3.3 EntityId → TimeFrameIndex Mapping

During scene building, construct a lookup from EntityId to its source time frame index. Both PointData and LineData store per-entry time indices, so the mapping can be built during the rebuildScene() pass and cached as std::unordered_map<EntityId, TimeFrameIndex>.


Phase 7: Subgroup Visibility Filtering

Status: Not started. Depends on Phase 5 (group integration) and Phase 6 (group coloring).

Goal: Allow the user to selectively show/hide points and lines by their group membership, enabling focused analysis of specific subgroups.

7.1 Visibility State

Extend state with a visibility filter:

struct TemporalProjectionViewStateData {
    // ... existing fields ...

    // Groups to show. Empty means "show all".
    std::vector<std::string> visible_groups;
    bool show_ungrouped = true;  ///< Show entities not in any group
};

7.2 Visibility Filtering During Scene Build

In rebuildScene(), before building the scene, filter entities:

For points:

  1. After SpatialMapper::mapAllPoints(), iterate MappedElement objects
  2. For each element, check if its EntityId belongs to a visible group
  3. Only include matching elements in the SceneBuilder::addGlyphs() call

For lines:

  1. After building LineBatchData, set visibility_mask[i] = 0 for lines whose EntityId does not belong to a visible group
  2. Upload the filtered visibility mask to BatchLineStore

The BatchLineRenderer already respects the visibility mask, so no rendering changes are needed for lines.

7.3 Properties Panel Controls

Add a “Visibility” collapsible section:

  • Group checklist — checkbox per group (checked = visible)
  • “Show ungrouped” checkbox — toggle visibility of entities not in any group
  • “Show All” / “Hide All” buttons — convenience toggles

7.4 Signal Flow

User toggles group visibility checkbox
        ↓
State updates visible_groups
        ↓
stateChanged() emitted
        ↓
rebuildScene() filters by visibility
        ↓
Filtered scene rendered

Phase 8: Per-Key Rendering Options

Status: Not started. Currently rendering options (point size, line width, color) are global.

Goal: Allow per-data-key rendering customization, so different PointData and LineData keys can have distinct visual styles.

8.1 Per-Key Options in State

Replace global options with per-key option maps:

struct TemporalProjectionViewStateData {
    // ... existing fields ...

    // Replace flat point_size/line_width with per-key options:
    std::map<std::string, PointGlyphOptions> point_options;   // key → glyph options
    std::map<std::string, LineRenderOptions> line_options;     // key → line options
};

Keys not present in the map use default PointGlyphOptions / LineRenderOptions.

8.2 Properties Panel

Replace the current flat spinboxes with a per-key options table, similar to LinePlotWidget’s per-series options table:

  • Point data table — add columns for glyph type, size, color
  • Line data table — add columns for color, width, alpha

8.3 Rendering Update

In rebuildScene(), look up per-key options when building glyph styles and line colors:

for (auto const & pb : point_batches) {
    auto opts = lookupPointOptions(pb.key, _state->pointOptions());
    CorePlotting::GlyphStyle style;
    style.glyph_type = toRenderableGlyphType(opts.glyph_type);
    style.size = opts.glyph_size;
    style.color = hexToGlmVec4(opts.hex_color, opts.alpha);
    builder.addGlyphs("points_" + pb.key, pb.mapped, style);
}

Dependency Graph

Phase 3: Double-Click Time Navigation
    │
    │   (all independent of each other, can be done in any order)
    │
Phase 4: Polygon Selection
    └── uses: PolygonInteractionController (already implemented)
    └── needs: CorePlotting::Selection::selectPointsInPolygon (new)
    │
Phase 5: Selection → Group Integration
    ├── depends on: Phase 4 (polygon selection adds to selection set)
    │   (but can start with click/stroke selection only)
    └── depends on: Plots/Common/GroupManagementPanel
        (shared with LinePlotWidget, ScatterPlotWidget)
    │
Phase 6: Group-Based Coloring
    └── depends on: Phase 5 (group membership required)
    │
Phase 7: Subgroup Visibility Filtering
    └── depends on: Phase 5 (group membership required)
    │
Phase 8: Per-Key Rendering Options
    └── depends on: Phase 2 (glyph/line options defined)

Phases 1–3 are independent and can be implemented in any order. Phase 4 (polygon selection) is independent of 1–3 but provides the most natural multi-select workflow. Phase 5 (group integration) can begin with existing click/stroke selection but benefits from Phase 4. Phases 6 and 7 both depend on Phase 5 but are independent of each other. Phase 8 extends Phases 1 and 2 to per-key granularity.


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/ TemporalProjectionViewWidget, ScatterPlotWidget
SelectionInstructions Implemented Plots/Common/SelectionInstructions.hpp TemporalProjectionViewWidget, ScatterPlotWidget
GroupManagementPanel ⏸️ Deferred N/A Both widgets use GroupContextMenuHandler via right-click; dedicated panel not needed yet
GroupColorMapper Planned CorePlotting/ TemporalProjectionViewWidget, LinePlotWidget, EventPlotWidget
Extended GlyphType enum Implemented CorePlotting/DataTypes/GlyphStyleData.hpp TemporalProjectionViewWidget, EventPlotWidget, ScatterPlotWidget, OnionSkinViewWidget
GlyphStyleControls widget Implemented Plots/Common/GlyphStyleWidget/ TemporalProjectionViewWidget, OnionSkinViewWidget, ScatterPlotWidget

Design Decisions

  1. Per-key vs. global rendering options: Phase 2 started with global options (simpler, now complete), extended to per-key options in Phase 8 to avoid premature complexity.

  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. Only a new selectPointsInPolygon() utility is needed for the ray-casting containment test.

Open Questions

  1. Group color conflict resolution: When an entity belongs to multiple groups, which group’s color takes priority? Options: first group alphabetically, most recently assigned, or a blended color. First-match is simplest.

  2. Visibility filtering performance: For very large datasets, should visibility filtering happen at the data-gathering stage (skip loading invisible entities entirely) or at the rendering stage (set visibility mask to 0)? The mask approach is simpler and sufficient for moderate dataset sizes.

  3. MaskData support: The state struct has a comment noting “mask_data_keys deferred due to CPU contour extraction performance”. If GPU-based contour extraction becomes available, MaskData rendering could be added as a future phase.

See Also