Skip to content

`TrackingPoint`

One measurement channel. Computes a single scalar from configured landmarks every frame. Referenced from PhaseConditions, FormRules, and angleBands.

class TrackingPoint {
final String id;
final String? name;
final TrackingPointType type;
final TrackingSide side;
final List<String> landmarks;
final TrackingPointConfig config;
final bool relativeToBaseline;
}

The 7 types

enum TrackingPointType {
angle, // 3-landmark joint angle
distance, // 2-landmark distance (image space)
proximity, // smoothed inverse distance
velocity, // rolling slope of a base channel
stability, // rolling max-min of a base channel
ratio, // ratio of two distances
position, // raw x or y of one landmark
}

angle

Three landmarks (A, B, C). Computes the angle at B between rays BA and BC, in degrees [0, 180].

const TrackingPoint(
id: 'left_knee',
type: TrackingPointType.angle,
landmarks: ['left_hip', 'left_knee', 'left_ankle'],
config: AngleTrackingConfig(targetValue: 90, tolerance: 20),
)

AngleTrackingConfig carries the trainer’s “expected” angle + a tolerance, used by the Studio’s preset chips and the form score aggregator. Doesn’t affect rep counting (the conditions on PhaseTransitions + FormRules drive that).

distance

Two landmarks. Computes 2D distance in normalised image space ([0, 1]).

const TrackingPoint(
id: 'stance_width',
type: TrackingPointType.distance,
landmarks: ['left_ankle', 'right_ankle'],
config: DistanceTrackingConfig(),
)

proximity

Smoothed inverse distance, 1 / (1 + distance). Used for “closeness” cues like wrist_to_shoulder (1.0 when touching, 0 when far apart). Two landmarks.

ratio

Four landmarks. Computes dist(landmarks[0], landmarks[1]) / dist(landmarks[2], landmarks[3]). Useful for proportional checks that survive scale variation:

const TrackingPoint(
id: 'stance_to_shoulder_ratio',
type: TrackingPointType.ratio,
landmarks: [
'left_ankle', 'right_ankle', // numerator: stance width
'left_shoulder', 'right_shoulder' // denominator: shoulder width
],
config: RatioTrackingConfig(),
)

Ratios are dimensionless so they’re independent of how far the user stands from the camera.

position

One landmark + an axis (x or y). Emits the raw normalised coordinate. Used for “where in the frame is the user” gates , e.g. lateral shuffle phases gated on hip_center.x.

const TrackingPoint(
id: 'hip_center_x',
type: TrackingPointType.position,
landmarks: ['left_hip'], // or right_hip, or midpoint computed elsewhere
config: PositionTrackingConfig(axis: TrackingAxis.x),
)

velocity

Derived from another tracking point. Rolling slope over a configurable window (default 200 ms). Used for jump detection, fast-movement cues.

const TrackingPoint(
id: 'wrist_y_velocity',
type: TrackingPointType.velocity,
landmarks: ['left_wrist'],
config: VelocityTrackingConfig(
baseTrackingPointId: 'left_wrist_y',
windowMs: 200,
),
)

stability

Rolling max-min of a base channel over a window (default 1000 ms). A small value means the channel is stable (good for hold phases).

const TrackingPoint(
id: 'spine_stability',
type: TrackingPointType.stability,
landmarks: [], // unused, the channel is derived
config: StabilityTrackingConfig(
baseTrackingPointId: 'spine_lean',
windowMs: 1000,
),
)

side

enum TrackingSide { left, right, both, any }

For angle and distance types, side indicates which anatomical side the tracking point references. both evaluates both sides and emits two values ({id}_left and {id}_right). any is a hint that direction doesn’t matter (used for view-invariant measurements).

relativeToBaseline

When true, the runtime captures the channel’s value at the START of each phase and emits current - baseline instead of the raw value. Used for movements where what matters is the CHANGE from the start of a phase, e.g. “vertical hip displacement during a jump” emits current_hip_y - hip_y_at_jump_start.

The baseline is per-phase and per-tracking-point, captured the first frame after the phase becomes active. See MovementTracker._baselineByPhase.

Channel ids

The id is what PhaseConditions and FormRules reference. It must be unique within the movement. The Studio uses the channel’s display name from the relevant *Definitions catalogue (e.g. AngleDefinitions) and stamps the id automatically; manual authors should pick stable kebab-case ids.

Catalogues

The Studio’s measurement picker is driven by per-type catalogues defined in package:pose_flow:

  • AngleDefinitions, 13 view-invariant flexion angles + several camera-frame angles (spine lean, knee valgus, hip tilt).
  • DistanceDefinitions, 12 anatomical distances (stance_width, wrist_spread, etc.).
  • RatioDefinitions, 8 dimensionless ratios.
  • PositionDefinitions, 14 single-landmark positions.
  • VelocityDefinitions, common velocity channels.
  • StabilityDefinitions, common stability channels.

See the measurement channels reference for the full catalogue + when to use each.