FrameState & PlayerPose

FrameState, PlayerPose, and H36M_JOINT_NAMES define the instantaneous per-frame kinematic and scoreboard contract used across MatchTimeline, PadelVisualizer, and the Universal Match Format (.parquet).

FrameState

Represents the complete state of the court, 9D ball, 4 players, and match scoreboard at a single timestamp timestamp.

  • Name
    timestamp
    Type
    float
    Description

    Elapsed time in seconds.

  • Name
    ball_position
    Type
    tuple[float, float, float]
    Description

    3D ball coordinates (x, y, z) in meters (CENTERED court frame).

  • Name
    ball_velocity
    Type
    tuple[float, float, float]
    Description

    3D linear ball velocity (vx, vy, vz) in m/s.

  • Name
    ball_spin
    Type
    tuple[float, float, float]
    Description

    3D angular ball velocity (wx, wy, wz) in rad/s.

  • Name
    ball_speed_mps / ball_speed_kmh
    Type
    float
    Description

    Computed scalar linear ball speed in m/s and km/h.

  • Name
    ball_spin_rpm
    Type
    float
    Description

    Computed total ball spin rate in revolutions per minute (RPM).

  • Name
    players
    Type
    dict[str, PlayerPose]
    Description

    Mapping of player IDs ("P1".."P4") to their PlayerPose instances.

  • Name
    score
    Type
    str
    Description

    Formatted scoreboard string displayed on the 3D HUD.

  • Name
    sets_a / sets_b
    Type
    int
    Description

    Completed sets won by Team A and Team B at this frame.

  • Name
    games_a / games_b
    Type
    int
    Description

    Games won in the active set by Team A and Team B.

  • Name
    points_a / points_b
    Type
    str
    Description

    Point call in the active game ("0", "15", "30", "40", "Ad", or tiebreak integer string).

  • Name
    server_team
    Type
    str
    Description

    Serving team identifier ("A" or "B").

  • Name
    server_player
    Type
    str
    Description

    Serving player identifier ("P1".."P4" or player name).

  • Name
    point_index
    Type
    int
    Description

    0-based rally/point index in the match progression.

  • Name
    in_tiebreak
    Type
    bool
    Description

    True when the active game is a tiebreak or super-tiebreak.

  • Name
    is_gold_point
    Type
    bool
    Description

    True on a deciding deuce point (40-40 with golden point enabled).

  • Name
    event
    Type
    str | None
    Description

    Optional discrete event label at this frame ("serve", "bounce_floor_sliding", "shot_smash", etc.).


methodframe.with_match_score()

Stamp Score from MatchScore

Returns a copy of the FrameState with all structured score fields (score, sets_a, sets_b, games_a, games_b, points_a, points_b, in_tiebreak, is_gold_point, point_index, server_team, server_player) populated directly from a MatchScore snapshot.

Arguments

  • Name
    match_score
    Type
    MatchScore
    Description

    Immutable MatchScore snapshot (e.g. match.current_score).

  • Name
    point_index
    Type
    int
    Description

    0-based rally index.

  • Name
    server_team
    Type
    str
    Description

    Serving team label ("A" or "B").

  • Name
    server_player
    Type
    str
    Description

    Serving player label.

  • Name
    golden_point_enabled
    Type
    bool
    Description

    If True, sets is_gold_point=True when points_a == "40" and points_b == "40".

Stamp MatchScore

method
with_match_score
from padelkit import FrameState, Match, TeamId

match = Match(advantage_method="gold_point")
match.history.add_point(TeamId.A)

frame = FrameState(
    timestamp=0.5,
    ball_position=(0.0, 2.0, 1.4),
).with_match_score(
    match.history.get_current_score(),
    point_index=1,
    server_team="A",
    server_player="P1",
)

print(frame.score, "| points:", frame.points_a, "-", frame.points_b)

PlayerPose

Represents the 3D position, heading, stroke animation state, and optional 17-joint H36M 3D skeletal pose of a single player at a single timestamp.

  • Name
    player_id
    Type
    str
    Description

    Player slot identifier ("P1", "P2", "P3", or "P4").

  • Name
    x / y / z
    Type
    float
    Description

    Root position (x, y, z) in court meters (CENTERED coordinate system).

  • Name
    yaw_deg
    Type
    float
    Description

    Facing heading in degrees (0.0 = facing +Y, 180.0 = facing -Y).

  • Name
    animation
    Type
    str
    Description

    Active locomotion or stroke clip ID ("idle", "run", "forehand_drive", "forehand_serve", etc.).

  • Name
    team
    Type
    str
    Description

    Team identifier ("A" for P1/P2, "B" for P3/P4).

  • Name
    swing_phase
    Type
    float
    Description

    Normalized stroke progress in [0.0, 1.0].

  • Name
    keypoints_3d
    Type
    tuple[tuple[float, float, float], ...] | None
    Description

    Optional 17-tuple of (jx, jy, jz) coordinates in the player's local frame (Pelvis XY at (0, 0), Z height above floor 0.0, facing +Y) following H36M_JOINT_NAMES.

  • Name
    world_keypoints_3d
    Type
    tuple[tuple[float, float, float], ...] | None
    Description

    Computed 17-tuple of (X, Y, Z) joints transformed into absolute 3D court coordinates, or None if keypoints_3d is not set.

Classmethods & Methods

  • Name
    from_world_keypoints_3d(player_id, world_keypoints_3d, ...)
    Type
    PlayerPose
    Description

    Constructs a PlayerPose directly from a (17, 3) array or sequence of absolute 3D world joints (e.g. from multi-camera triangulation or MotionBERT), automatically anchoring (x, y) at Pelvis (joint 0), z at the lowest ankle, and computing local keypoints_3d.

  • Name
    to_dict()
    Type
    dict[str, Any]
    Description

    Serializes the pose for the Three.js visualizer (including kp3d when keypoints_3d is populated).

Construct from World 3D Joints

classmethod
from_world_keypoints_3d
import numpy as np
from padelkit import H36M_JOINT_NAMES, PlayerPose

print("17 H36M joints:", H36M_JOINT_NAMES)

# Example (17, 3) absolute world coordinates in court meters
world_j3d = np.zeros((17, 3), dtype=np.float32)
world_j3d[:, 0] = 1.85   # X = +1.85m
world_j3d[:, 1] = -7.20  # Y = -7.20m
world_j3d[0, 2] = 0.88   # Pelvis Z = 0.88m
world_j3d[10, 2] = 1.66  # Head Z = 1.66m

pose = PlayerPose.from_world_keypoints_3d(
    player_id="P1",
    world_keypoints_3d=world_j3d,
    yaw_deg=10.0,
    animation="forehand_drive",
    team="A",
    swing_phase=0.5,
)

print("Root (x, y, z):", (pose.x, pose.y, pose.z))
print("Pelvis offset:", pose.keypoints_3d[0])
print("World Head Z:", pose.world_keypoints_3d[10][2])

Was this page helpful?