`PhaseStateMachine`
PhaseStateMachine is the internal state machine
MovementTracker uses to track which
phase of an movement the user is currently in. It runs in one of two
modes depending on the loaded Movement:
- Rule-based mode (
phaseConfigspopulated,positionsempty) , the default and only mode produced by PoseFlow Studio. Phase transitions fire when the authored condition gates on each phase evaluate to true against the current per-frame tracking values. - Vector-matching mode (
Movement.positions.isNotEmpty), legacy movements built from a recorded marker pass. A small PCA index classifies the current pose against named reference positions; phase transitions fire when the user traverses the authored sequence of positions.
You rarely instantiate PhaseStateMachine directly, MovementTracker
owns it. This page documents the surfaces that leak out of the
tracker so consumers can render phase-aware UIs.
What you see through the tracker
final result = tracker.processFrame(pose);// result.phaseId, current phase id, null until the first transitionPlus the rep stream:
tracker.onRepCompleted.listen((event) { // fires when the phase machine completes a rep per the loaded // movement's RepDetectionConfig});Mode selection
The mode is picked once at tracker.load(movement) time and held
for the session:
final movement = Movement.fromJsonString(json);tracker.load(movement);// If movement.positions.isNotEmpty → vector-matching mode// Otherwise → rule-based modeSwitching modes mid-session requires unload + reload with a different movement.
Rule-based mode
Each PhaseConfig carries a list of PhaseConditions (membership
gates: “current tracking-point values must fall inside these
bands”). On every frame, the state machine evaluates the conditions
of the current phase’s authored transitions; the first satisfied
transition wins, and the machine moves to that transition’s
toPhase.
PhaseConfig.duration (optional minimum-time-in-phase) holds the
machine in place, even if the next transition’s conditions are
satisfied, the machine waits the duration before firing.
PhaseCondition and PhaseConfig are
the per-phase authoring surfaces.
Vector-matching mode
Reference positions are loaded into a small in-memory PCA index. On every frame, the current pose’s landmark vector is projected and the nearest reference is returned. The state machine fires a transition whenever the nearest-position label changes.
The PCA index lives inside the runtime; it’s built fresh each time a movement is loaded. Lookups are ~300K matches/sec on a modern phone , this mode is fast.
Rep boundaries
The state machine fires RepCompletedEvent when the loaded
movement’s RepDetectionConfig
says a rep is done:
| Strategy | Rep boundary |
|---|---|
cycle | The user re-enters primaryPhase. |
sequence | The user has traversed every phase in phases in order. |
compound | The user has traversed any sequence in sequences. |
hold | durationIncrement ms have elapsed while inside primaryPhase. |
The fired event carries the per-dimension RepQuality breakdown
(form / ROM / tempo / stability), see
TrackingResult.
Why this is internal
PhaseStateMachine does not have a stable public constructor, the
tracker creates it from a loaded Movement, owns its lifecycle, and
funnels events back to consumers. The internal API surface evolves
without back-compat guarantees; trying to instantiate one by hand
will break across minor versions.
If you need a deterministic test harness for phase logic, drive a
real MovementTracker with synthetic Pose fixtures and assert
against tracker.onRepCompleted and result.phaseId. See
Rep counting parity for the test
patterns the SDK itself uses.
Read next
MovementTracker, owns the state machine.- Rep counting, algorithm walkthrough.
RepDetectionConfig, the config that decides what counts as a rep.PhaseConfig, per-phase authoring shape.