Physics-Balls
view release on metacpan or search on metacpan
t/lib/Presets.pm view on Meta::CPAN
vmax => 8, g => 9.81,
},
);
sub names { return qw/pool snooker/ }
sub desc { my ($name) = @_; return $DESC{$name} }
sub table {
my ($name) = @_;
my $d = $DESC{$name} or die "Presets: no table $name";
return Physics::Balls::Table->new(L => $d->{L}, W => $d->{W}, R => $d->{R}, corner => $d->{corner}, side => $d->{side});
}
sub constants {
my ($name) = @_;
my $d = $DESC{$name};
return (mu => $d->{mu}, e => $d->{e}, vmax => $d->{vmax}, g => $d->{g});
}
sub world_built {
my ($name) = @_;
return Physics::Balls::World->from_table(table($name), constants($name));
}
sub fixture_dir { return "$FindBin::Bin/fixtures" }
sub read_json {
my ($file) = @_;
open my $fh, '<', $file or die "$file: $!";
my $text = do { local $/; <$fh> };
close $fh;
return JSON::PP->new->decode($text);
}
sub hex_to_nv { my ($h) = @_; return unpack 'd>', pack 'H*', $h }
# Events an instant apart are one instant: the engine orders two rolls at the
# same time by which queue entry came first, and a build whose doubles differ
# in the last place orders them the other way. Before an index-by-index
# comparison each run of events within $tol of its neighbour is sorted by kind
# and participants, on both sides the same, so the contract is the order of
# what happens and not the order of what happens at once.
sub settle_ties {
my ($events, $tol) = @_;
my (@out, @run);
my $flush = sub {
push @out, sort { join("\0", @$a[1 .. $#$a]) cmp join("\0", @$b[1 .. $#$b]) } @run;
@run = ();
};
for my $e (@$events) {
$flush->() if @run && abs($e->[0] - $run[-1][0]) >= $tol;
push @run, $e;
}
$flush->();
return \@out;
}
# The marks that compare against recorded doubles hold only where the compiler
# rounds every operation to a double; a build left on the x87 unit lands a few
# ulps off in the first segment and a collision cascade carries it anywhere.
# The reason to skip those marks, or nothing.
sub wide_doubles {
my $m = Physics::Balls->float_eval_method;
return $m > 0 ? "FLT_EVAL_METHOD is $m: this build evaluates a double wider than a double, and the fixtures were recorded at 64 bits" : '';
}
sub geometry {
my ($name) = @_;
return read_json(fixture_dir() . "/geometry-$name.json");
}
sub world_exact {
my ($name) = @_;
my $g = geometry($name);
my $w = $g->{world};
my $hx = $g->{hex};
my $conv = sub {
my ($rows, $hexrows) = @_;
return [ map { my $i = $_; [ map { hex_to_nv($hexrows->[$i][$_]) } 0 .. $#{ $rows->[$i] } ] } 0 .. $#$rows ];
};
my $d = $DESC{$name};
return Physics::Balls::World->new(
L => hex_to_nv($hx->{L}), W => hex_to_nv($hx->{W}), R => hex_to_nv($hx->{R}),
walls => $conv->($w->{walls}, $hx->{walls}),
noses => $conv->($w->{noses}, $hx->{noses}),
gates => $conv->($w->{gates}, $hx->{gates}),
constants($name),
);
}
sub fixtures {
my $dir = fixture_dir();
opendir my $dh, $dir or die "$dir: $!";
my @files = sort grep { /\.json\z/ && !/^geometry-/ } readdir $dh;
closedir $dh;
return map { read_json("$dir/$_") } @files;
}
1;
( run in 1.461 second using v1.01-cache-2.11-cpan-e7c6538aa59 )