How it works
Every camera frame goes through the same chain. No piece knows more about the others than it needs to, which is why each one can be tested and replaced on its own.
1. The pose source
Section titled “1. The pose source”A PoseProvider delivers an AsyncStream<Pose>. It can be the camera (VisionPoseProvider), a recording (RecordedPoseProvider) or anything you implement. Each Pose is one frame of body points, in pixels and with sides already corrected.
2. The session
Section titled “2. The session”MotionSession is the front door. For every pose:
- Visibility. Are all the joints the exercise measures in view? The change is confirmed after a few frames in a row (~0.25 s) so it doesn’t flicker.
- Placement.
SetupCheckchecks whether the patient faces the camera or stands sideways, is too far away or at the edge of the image. - Engine.
RepEnginecomputes the primary measure, smooths it, checks the form rules and decides whether a rep finished. - Baseline. If one is running, it updates its progress.
Then it publishes the result in two ways.
3. Snapshot and events
Section titled “3. Snapshot and events”MotionSnapshot |
MotionEvent |
|
|---|---|---|
| What it is | The whole state, in one value | Something that happened on this frame |
| How to read it | session.snapshot (observable) |
for await event in session.events() |
| What for | Drawing: angle, counter, progress ring | Reacting once: say “nice!”, vibrate, save |
More detail in Events and snapshot.
Exercises are data
Section titled “Exercises are data”The engine has no exercise hard-coded. An ExerciseDefinition describes:
- what to measure (
primary, aMeasure), - how to count (
kind: reps or hold), - what form to require (
rules, a list ofFormRule), - where to look from (
view: side or front).
Since everything is Codable, a new exercise can arrive as JSON from your server. See Custom exercises.