Skip to content

Refactor: Extract overlay tools state into OverlayToolsModel - #59

Merged
veillette merged 4 commits into
mainfrom
claude/review-mvc-architecture-ofcYw
Feb 26, 2026
Merged

Refactor: Extract overlay tools state into OverlayToolsModel#59
veillette merged 4 commits into
mainfrom
claude/review-mvc-architecture-ofcYw

Conversation

@veillette

Copy link
Copy Markdown
Collaborator

Summary

This refactor extracts all measurement and coordinate-system overlay tool state from SimModel into a new dedicated OverlayToolsModel class. This separation of concerns keeps video playback and track management logic distinct from geometric tool state, improving code organization and maintainability.

Key Changes

New Model Layer

  • Created OverlayToolsModel: A new class that owns all reactive state for overlay tools:

    • Coordinate system (origin, angle)
    • Calibration ruler (endpoints, distance, unit)
    • Measuring tape (endpoints)
    • Angle tool (vertex, arms)
    • Visibility toggles for all tools
    • Derived model-view transform and unit-string properties
    • Helper method clampCoordOrigin() for constraining origin to video bounds
  • Re-exported calibration constants from OverlayToolsModel in SimModel to maintain backward compatibility for existing importers

SimModel Refactoring

  • Removed all overlay tool properties and moved them to OverlayToolsModel
  • Added public readonly overlayTools = new OverlayToolsModel() as the single source of truth
  • Updated modelViewTransformProperty references to use overlayTools.modelViewTransformProperty
  • Made tracker private (was public) and added resizeTracker() and resetTracker() public methods for controlled access
  • Added activateRecording() and activateUpload() methods to atomically set video properties
  • Added addTrackAndActivate() convenience method for auto-tracking workflow
  • Moved prevModelViewTransform cache to constructor scope for cleaner organization

View Layer Updates

  • Updated all view nodes to access overlay properties via model.overlayTools.*:

    • SimScreenView, VideoPlayerNode, AutoTrackerNode
    • CalibrationToolNode, CoordinateSystemNode
    • MeasuringTapeNode, AngleToolNode
    • ControlPanel, MeasurementToolsPanel
  • Updated graph/kinematics properties to use model.overlayTools.calibUnitProperty

Data Model Improvements

  • Track color handling: Changed Track.color from CSS string to colorIndex (integer), with colors resolved at display time from TRACK_COLORS array
  • Video recording/upload labels: Changed from label string to num (number) with formatting helpers in VideoSourceControlNode
  • Added UploadedVideo.name field to store original filename separately from display label

New Utility Module

  • Created TrackExporter: Pure functions for CSV export logic extracted from DataTableNode:
    • buildDataRows(): Collects unique frames across all tracks
    • generateCsv(): Generates CSV text with proper formatting
    • Enables reuse of export logic independent of view layer

Utility Function Relocation

  • Moved getAnimatedWebPInfo() from VideoSourceControlNode to webcam.ts for reusability

Notable Implementation Details

  • The OverlayToolsModel constructor initializes all tool positions in video-local coordinates, ensuring consistent coordinate space across all overlays
  • Track point retransformation logic remains in SimModel but now uses overlayTools.modelViewTransformProperty for MVT changes
  • All view nodes that previously accessed overlay properties directly now go through the overlayTools object, creating a clear dependency graph
  • CSV export logic is now testable independently of the UI layer

https://claude.ai/code/session_01LAwGKZUVqF9MNrUgaFtFyS

…tion, simplify track auto-creation

Issues addressed from architecture review (#1.1, #1.2, #1.3):

**1.2 — Tracker encapsulation (SimModel)**
- Make `tracker` private; no view can access OpenCVTracker directly
- Add facade methods: `isTrackerReady`, `resetTracker()`, `resizeTracker()`,
  `initTracker()`, `trackFrame()` — the full tracker API now lives behind
  the model boundary

**1.1 — Atomic video source activation (SimModel + VideoSourceControlNode)**
- Add `activateRecording()`, `activateUpload()`, `activateBundledVideo()`
  that set all related properties (isWebcamVideo, frameRate, totalFrameCount,
  currentWebcamBlob) in a single call, eliminating visible intermediate states
- Consolidate all activation logic into the `selectedVideoProperty.lazyLink`
  handler — the single canonical dispatch point
- Remove scattered direct property writes from the webp upload handler,
  `storeAndLoad`, and the webcam panel `onVideoReady` callback
- Fix pre-existing bug: `onWebcamReady` was being called twice in the webcam
  recording and file upload flows (once from the lazyLink, once directly)
- `VideoSelectedCallback` type simplified to `(url: string) => void` since
  fps is now owned by `activateBundledVideo`

**1.3 — Track auto-creation (SimModel + AutoTrackerNode)**
- Add `addTrackAndActivate()` to SimModel; replaces the 8-line view-side
  block that read `tracksProperty.value` directly after `addTrack()` to
  find and activate the newest track

https://claude.ai/code/session_01LAwGKZUVqF9MNrUgaFtFyS
Issues addressed:
- **2 + 3**: Extract OverlayToolsModel from SimModel — all geometric overlay
  state (axes, calibration, tape, angle, coord system, MVT, unit strings) now
  lives in a dedicated OverlayToolsModel owned by SimModel. Remove the
  identity distanceUnitProperty; callers use calibUnitProperty directly.
- **1.4**: Remove label formatting from model layer — WebcamRecording/
  UploadedVideo no longer carry a pre-formatted 'label' string. Types gain
  'num: number' and 'name: string' fields; formatRecordingLabel/
  formatUploadLabel helpers in VideoSourceControlNode format on the fly.
- **1.5**: Extract TrackExporter.ts — DataRow type, buildDataRows(), and
  generateCsv() move from DataTableNode (view) to the model layer.
- **4**: Add VideoPlayerNode.addVideoOverlay() and make videoContentLayer
  private; SimScreenView no longer reaches into the layer directly.
- **5**: Move getAnimatedWebPInfo() from VideoSourceControlNode to webcam.ts;
  imported back via the established webcam module boundary.
- **6**: Replace Track.color (CSS string) with Track.colorIndex (number);
  all consumers look up TRACK_COLORS[colorIndex] at render time.
- **7**: Add optional targetTicks param to GraphDataManager.calculateTickSpacing;
  PlaybackControlsNode uses it (targetTicks=15) instead of a local duplicate.
- **8**: Extract keyboard shortcut handler from VideoPlayerNode constructor
  into a private createKeyboardHandler() factory method.

https://claude.ai/code/session_01LAwGKZUVqF9MNrUgaFtFyS
- Replace TRACK_COLORS[n]! assertions with getTrackColor() helper in TrackLabColors.ts
- Remove unused re-export of calibration constants from SimModel.ts (noBarrelFile)
- Fix unused import block (CALIBRATION_DISTANCE_RANGE etc.) in SimModel.ts
- Use tracks[length-1] instead of .at(-1) for TypeScript target compatibility
- Add await in trackFrame() to satisfy biome useAwait rule
- Fix GraphDataManager named-vs-default import in PlaybackControlsNode.ts
- Apply formatter corrections (4 files touched by biome format)

Result: tsc --noEmit passes, biome lint 0 errors 0 warnings, biome format no changes.

https://claude.ai/code/session_01LAwGKZUVqF9MNrUgaFtFyS
Biome merged the two separate TrackLabColors imports and reordered
the TrackExporter imports to match the project's canonical style.

https://claude.ai/code/session_01LAwGKZUVqF9MNrUgaFtFyS
@veillette
veillette merged commit 0c8a5a7 into main Feb 26, 2026
1 check passed
@veillette
veillette deleted the claude/review-mvc-architecture-ofcYw branch March 1, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants