def simulate(
initial_ball: BallState,
strokes_a: List[StrokeParams],
strokes_b: List[StrokeParams],
table: Table,
net: Net,
dt: float = TIME_STEP,
max_time: float = MAX_TIME,
record_interval: int = RECORD_INTERVAL,
) -> SimulationResultParameters:
initial_ball: Initial ball statestrokes_a: Stroke sequence for Player Astrokes_b: Stroke sequence for Player Btable: Table configurationnet: Net configurationdt: Time step (seconds)max_time: Maximum simulation time (seconds)record_interval: Record interval (steps)
Returns: SimulationResult object containing complete trajectory data
def aerodynamic_acceleration(velocity: np.ndarray, omega: np.ndarray) -> np.ndarrayCalculates aerodynamic acceleration including gravity, drag, and Magnus force.
Parameters:
velocity: Ball velocity vector (m/s)omega: Ball angular velocity vector (rad/s)
Returns: Acceleration vector (m/s²)
def rk4_step(state: BallState, dt: float) -> BallStateAdvances state by one step using 4th-order Runge-Kutta method.
Parameters:
state: Current ball statedt: Time step
Returns: New ball state
def handle_plane_collision(
state: BallState,
plane_point: np.ndarray,
normal: np.ndarray,
restitution: float,
friction: float,
surface_velocity: np.ndarray,
) -> boolHandles collision between ball and planar surface.
Parameters:
state: Ball state (will be modified)plane_point: A point on the planenormal: Plane normal vectorrestitution: Normal restitution coefficientfriction: Tangential friction coefficientsurface_velocity: Surface velocity
Returns: Whether collision impulse was applied
def create_stroke_from_mode(
player: Player,
mode: str = "topspin",
rubber_type: RubberType = RubberType.INVERTED,
overrides: Optional[Dict[str, Any]] = None,
) -> StrokeParamsCreates stroke parameters from predefined modes.
Parameters:
player: Player (A or B)mode: Stroke mode ("flick", "topspin", "backspin", etc.)rubber_type: Rubber typeoverrides: Parameter override dictionary
Returns: StrokeParams object
def compute_racket_for_stroke(
ball_pos: np.ndarray,
ball_vel: np.ndarray,
stroke: StrokeParams,
player: Player,
table_height: float,
) -> RacketStateComputes racket state required for executing a stroke.
Parameters:
ball_pos: Ball positionball_vel: Ball velocitystroke: Stroke parametersplayer: Hitting playertable_height: Table height
Returns: Racket state configuration
def create_table() -> TableCreates standard ITTF table tennis table configuration.
Returns: Table object
def create_net() -> NetCreates standard ITTF table tennis net configuration.
Returns: Net object
def create_custom_scenario(
position: Optional[np.ndarray] = None,
velocity: Optional[np.ndarray] = None,
omega: Optional[np.ndarray] = None,
strokes_a: Optional[List[StrokeParams]] = None,
strokes_b: Optional[List[StrokeParams]] = None,
) -> Tuple[BallState, List[StrokeParams], List[StrokeParams]]Creates custom simulation scenario.
Parameters:
position: Initial positionvelocity: Initial velocityomega: Initial angular velocitystrokes_a: Stroke sequence for Player Astrokes_b: Stroke sequence for Player B
Returns: (ball state, A stroke sequence, B stroke sequence)
def plot_trajectory_3d(
result: SimulationResult,
table: Table,
net: Net,
ball_color: str = DEFAULT_BALL_COLOR,
ball_size: float = DEFAULT_BALL_SIZE,
scene_margin: float = DEFAULT_SCENE_MARGIN,
) -> plt.FigureCreates 3D trajectory plot.
Parameters:
result: Simulation resultstable: Table configurationnet: Net configurationball_color: Ball colorball_size: Ball sizescene_margin: Scene margin
Returns: matplotlib Figure object
def animate_trajectory_3d(
result: SimulationResult,
table: Table,
net: Net,
filename: str,
fps: int = ANIM_FPS,
skip: int = ANIM_SKIP,
ball_color: str = DEFAULT_BALL_COLOR,
ball_size: float = DEFAULT_BALL_SIZE,
scene_margin: float = DEFAULT_SCENE_MARGIN,
) -> NoneCreates 3D trajectory animation.
Parameters:
result: Simulation resultstable: Table configurationnet: Net configurationfilename: Output filenamefps: Frames per secondskip: Frame skip interval
@dataclass
class BallState:
position: np.ndarray # (x, y, z) position
velocity: np.ndarray # (vx, vy, vz) velocity
omega: np.ndarray # (ωx, ωy, ωz) angular velocity@dataclass
class Table:
height: float # table surface height
length: float # length (x direction)
width: float # width (y direction)
restitution: float # restitution coefficient
friction: float # friction coefficient@dataclass
class StrokeParams:
target_x: float # target x position for stroke
strike_height: float # strike height
racket_angle: float # racket angle (radians)
swing_speed: float # swing speed
swing_direction: np.ndarray # swing direction
rubber_type: RubberType # rubber type
spin_intent: str # spin intent
mode: str = "custom" # stroke mode@dataclass
class SimulationResult:
ball_history: Dict[str, np.ndarray] # ball trajectory history
racket_a_history: Dict[str, np.ndarray] # Player A racket history
racket_b_history: Dict[str, np.ndarray] # Player B racket history
events: List[Tuple[float, EventType, str]] # event list
net_crossings: int # net crossing count
table_bounces: int # table bounce count
rally_count: int # rally count
final_event: EventType # final event
winner: Optional[Player] = None # winner
winner_reason: str = "" # win reasonINVERTED: Inverted rubber (high spin, offensive)PIMPLED: Pimpled rubber (medium spin, control)ANTISPIN: Anti-spin rubber (low spin, defensive)
NONE = 0: No eventTABLE_BOUNCE = 1: Table bounceRACKET_A_HIT = 2: Player A hitRACKET_B_HIT = 3: Player B hitNET_HIT = 4: Net touchNET_CROSS_SUCCESS = 5: Successful net crossingNET_CROSS_FAIL = 6: Net crossing failureOUT_OF_BOUNDS = 7: Out of boundsDOUBLE_BOUNCE = 8: Double bounce
A: Player A (negative x side)B: Player B (positive x side)