NAME

Physics::Balls - balls on a table with friction, walls and pockets

VERSION

Version 0.01

SYNOPSIS

use Physics::Balls;

my $table = Physics::Balls::Table->new(
    L => 2.540, W => 1.270, R => 0.028575,
    corner => { mouth => 0.1175, jaw_deg => 142, shelf => 0.041 },
    side   => { mouth => 0.1302, jaw_deg => 104, shelf => 0.008 },
);
my $world = Physics::Balls::World->from_table($table,
    mu => { s => 0.2, r => 0.02, sp => 0.044 },
    e  => { bb => 0.95, c => 0.8, cf => 0.2, rc => 0.7 },
    vmax => 8, g => 9.81,
);

my $out = Physics::Balls->strike($world,
    layout => [ [0, 63500, 63500], [1, 190500, 63500] ],
    ball => 0, dx => 1_000_000, dy => 0, power => 520, sx => 0, sy => 0,
);
if ($out->error) { die $out->message }
print $out->t, " seconds, ", scalar @{ $out->events }, " events\n";
my $payload = $out->to_payload;    # what a game stores and a client plays back

DESCRIPTION

An engine for round balls of one size on a flat surface: sliding and rolling friction, side spin, walls a ball bounces off, nose points it can rattle against, and gates it drops through. It knows nothing about any game. A pool or snooker adapter describes its table, sends a strike, and reads the outcome: the contacts in order, where every ball came to rest, which balls dropped and where, and the trajectory as segments a client can play back without simulating anything itself.

The simulation is event-based and analytic. A sliding ball is a parabola under constant deceleration, a rolling ball a straight line, and every contact time is the first downward zero of a polynomial found by a fixed number of bisections in a fixed order. The same inputs give the same outcome, bit for bit, on every platform that keeps IEEE doubles honest, which is why the inputs are integers and the build refuses fused multiply-adds.

Units

A description is in metres, seconds and metres per second. A layout and a rest position are integers in hundredths of a millimetre. A shot is integers: a direction dx, dy in plus or minus 1,000,000, power 0 to 1000, and a tip offset sx, sy in thousandths of the radius from -500 to 500. Segments are doubles in metres and seconds.

METHODS

strike

my $out = Physics::Balls->strike($world, layout => \@layout, %shot);

Builds a Physics::Balls::Strike from %shot, validates it against the layout, and plays it on the world. Returns a Physics::Balls::Outcome, or a Physics::Balls::Error when the shot or the layout is refused; both answer error, so $out->error is the one test a caller needs.

abi_version

The version of the C table in pb_abi.h.

THE C INTERFACE

include/pb_abi.h is installed with the module and describes a versioned function-pointer table, resolved at runtime through Physics::Balls::Engine::_abi_ptr, so another module can roll balls with no Perl frame in between. The engine itself is perl-free and the same C compiles alone.

SEE ALSO

Physics::Balls::World, Physics::Balls::Table, Physics::Balls::Strike, Physics::Balls::Outcome, Physics::Balls::Error.

AUTHOR

LNATION, <email at lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)