Skip to content

refactor(model): extract kinematics and MVT logic out of SimModel - #22

Merged
veillette merged 3 commits into
mainfrom
claude/review-codebase-architecture-uT4Js
Feb 22, 2026
Merged

refactor(model): extract kinematics and MVT logic out of SimModel#22
veillette merged 3 commits into
mainfrom
claude/review-codebase-architecture-uT4Js

Conversation

@veillette

Copy link
Copy Markdown
Collaborator

SimModel was doing too much — mix of state management, math, and physics.

  • Extract computeTrackKinematics + finiteDifference into
    KinematicsComputer.ts (pure functions, no Axon/SceneryStack deps)
  • Extract buildModelViewTransform into ModelViewTransformFactory.ts
    (pure function, no Axon/SceneryStack deps)
  • SimModel now imports and delegates to these modules; drops Matrix3,
    MIN_CALIB_DISTANCE, MIN_PIXEL_DISTANCE, and KinematicPoint
    from its own import list

SimModel retains all Properties and wiring — only the math implementations
move. Net result: SimModel shrinks from 475 → 341 lines; each extracted
module is independently readable and testable.

https://claude.ai/code/session_01P58fvbxUgRZn9eqbgDL4uV

SimModel was doing too much — mix of state management, math, and physics.

- Extract `computeTrackKinematics` + `finiteDifference` into
  `KinematicsComputer.ts` (pure functions, no Axon/SceneryStack deps)
- Extract `buildModelViewTransform` into `ModelViewTransformFactory.ts`
  (pure function, no Axon/SceneryStack deps)
- SimModel now imports and delegates to these modules; drops `Matrix3`,
  `MIN_CALIB_DISTANCE`, `MIN_PIXEL_DISTANCE`, and `KinematicPoint`
  from its own import list

SimModel retains all Properties and wiring — only the math implementations
move. Net result: SimModel shrinks from 475 → 341 lines; each extracted
module is independently readable and testable.

https://claude.ai/code/session_01P58fvbxUgRZn9eqbgDL4uV
The old type had two optional fields (property, accessor) with no static
guarantee that at least one was present. ConfigurableGraph.getValueForAxis
branched on accessor truthiness then fell back to property?.value ?? null —
defensive code that hid a missing constraint.

Replace with two concrete named types:

  RecordPlottable — carries an accessor function; value extracted from a
                    data-point record (all current kinematics quantities).
  LivePlottable   — carries a TReadOnlyProperty<number>; value read live.

PlottableProperty is now their union. TypeScript narrows the correct branch
in getValueForAxis via 'accessor' in axisProperty; no optional chaining or
null fallback remains. A PlottableProperty with neither field is now a
compile-time error rather than a silent runtime null.

KinematicsGraphNode.createPlottableProperty already returns { name, unit,
accessor }, satisfying RecordPlottable without any changes.

https://claude.ai/code/session_01P58fvbxUgRZn9eqbgDL4uV
The MVT re-projection logic was an inline anonymous lambda with a single
line of explanation.  Any contributor writing to tracksProperty without
reading that specific listener would silently corrupt point positions.

Two changes:

1. Extract the re-projection loop into retransformTrackPoints(prev, next).
   The JSDoc explains the invariant, the pixel → model math, and the
   warning about direct writes to tracksProperty.

2. Add an INVARIANT comment block at the tracksProperty declaration — the
   first thing a contributor reads when they find the field — summarising
   the constraint and pointing to retransformTrackPoints().

The listener itself shrinks to two lines; all the 'why' is now in the
named method.  Behaviour is identical.

https://claude.ai/code/session_01P58fvbxUgRZn9eqbgDL4uV
@veillette
veillette merged commit 72b3a70 into main Feb 22, 2026
1 check passed
@veillette
veillette deleted the claude/review-codebase-architecture-uT4Js branch February 22, 2026 19:39
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