Data-SpatialHash-Shared

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


    "new_memfd" creates a Linux memfd (anonymous but transferable via
    "memfd" file descriptor). "new_from_fd" reopens an existing memfd in
    another process.

  Mutators
        my $h = $s->insert(x, y, value);        # 2D insert -- returns handle or undef
        my $h = $s->insert(x, y, z, value);     # 3D insert
        my $h = $s->insert(x, y, z, value, r);  # 3D insert with an interaction radius
        $s->set_radius($h, $r);                 # set/replace an entry's radius
        $s->move($h, x, y);                     # relocate entry (2D)
        $s->move($h, x, y, z);                  # relocate entry (3D)
        $s->remove($h);                         # free entry slot
        $s->set_value($h, $v);                  # update stored value
        $s->clear;                              # remove all entries
        my @ids = $s->insert_many([ [x,y,value], [x,y,value,r], ... ]);  # bulk insert
        my $n   = $s->move_many([ [handle,x,y], [handle,x,y,z], ... ]);  # bulk move

    "insert" returns a handle (opaque integer) on success, or "undef" if
    "max_entries" is exhausted. "move" and "remove" return true on success,
    or false if the handle is invalid or already removed. "set_value"
    instead croaks on an invalid or freed handle; it and "clear" return

eg/moving_entities.pl  view on Meta::CPAN


my (%h, %pos, @vel);   # id => handle / [x,y] / [vx,vy]
for my $id (1 .. $N) {
    my @p = (rand()*$WORLD, rand()*$WORLD);
    $h{$id}   = $s->insert(@p, $id);
    $pos{$id} = [@p];
    $vel[$id] = [ (rand()*2-1)*2, (rand()*2-1)*2 ];
}

for my $tick (1 .. 5) {
    for my $id (keys %h) {                       # advance + relocate, bouncing off walls
        my $p = $pos{$id};
        for my $a (0, 1) {
            $p->[$a] += $vel[$id][$a];
            if    ($p->[$a] < 0)      { $p->[$a] = -$p->[$a];            $vel[$id][$a] *= -1 }
            elsif ($p->[$a] > $WORLD) { $p->[$a] = 2*$WORLD - $p->[$a];  $vel[$id][$a] *= -1 }
        }
        $s->move($h{$id}, @$p);
    }
    my (%seen, $pairs);                            # broad-phase collision candidates
    for my $id (keys %h) {

lib/Data/SpatialHash/Shared.pm  view on Meta::CPAN

C<new_memfd> creates a Linux memfd (anonymous but transferable via C<memfd>
file descriptor).  C<new_from_fd> reopens an existing memfd in another
process.

=head2 Mutators

    my $h = $s->insert(x, y, value);        # 2D insert -- returns handle or undef
    my $h = $s->insert(x, y, z, value);     # 3D insert
    my $h = $s->insert(x, y, z, value, r);  # 3D insert with an interaction radius
    $s->set_radius($h, $r);                 # set/replace an entry's radius
    $s->move($h, x, y);                     # relocate entry (2D)
    $s->move($h, x, y, z);                  # relocate entry (3D)
    $s->remove($h);                         # free entry slot
    $s->set_value($h, $v);                  # update stored value
    $s->clear;                              # remove all entries
    my @ids = $s->insert_many([ [x,y,value], [x,y,value,r], ... ]);  # bulk insert
    my $n   = $s->move_many([ [handle,x,y], [handle,x,y,z], ... ]);  # bulk move

C<insert> returns a handle (opaque integer) on success, or C<undef> if
C<max_entries> is exhausted.  C<move> and C<remove> return true on success,
or false if the handle is invalid or already removed.  C<set_value> instead
croaks on an invalid or freed handle; it and C<clear> return nothing.

t/17-bulk.t  view on Meta::CPAN

use strict; use warnings; use Test::More;
use Data::SpatialHash::Shared;

# --- move_many ---
{
    my $s = Data::SpatialHash::Shared->new(undef, 100, 0, 1.0);
    my @h = map { $s->insert($_, 0, $_) } 1..5;
    my $moved = $s->move_many([ map { [ $h[$_-1], $_+10, $_+10 ] } 1..5 ]);
    is $moved, 5, 'move_many reports 5 moved';
    is_deeply [$s->position($h[0])], [11, 11, 0], 'move_many relocated entry (2D row)';
    $s->move_many([[ $h[1], 1, 2, 3 ]]);                       # 3D row
    is_deeply [$s->position($h[1])], [1, 2, 3], 'move_many 3D row';
    $s->remove($h[2]);
    my $m2 = $s->move_many([[ $h[2], 5, 5 ], [ $h[3], 6, 6 ]]); # dead handle skipped
    is $m2, 1, 'move_many skips a freed handle, counts the live one';
    is_deeply [$s->position($h[3])], [6, 6, 0], 'live handle in mixed batch moved';
    is $s->move_many([]), 0, 'move_many([]) moves nothing';
}

# --- insert_many ---

t/18-geo.t  view on Meta::CPAN

ok abs($pp[2] - 500) < 1e-3,     'north pole altitude round-trips';

# +-pi meridian: longitude magnitude round-trips to ~pi
my @pm = $s->position_geo($s->insert_geo(0.3, $PI, 0, -2));
ok abs(abs($pm[1]) - $PI) < 1e-6, '+-pi meridian longitude magnitude round-trips';

# move_geo
my $hmv = $s->insert_geo(0.1, 0.1, 0, -3);
ok $s->move_geo($hmv, 0.2, 0.2, 1000), 'move_geo returns true';
my @mp = $s->position_geo($hmv);
ok abs($mp[0] - 0.2) < 1e-9 && abs($mp[1] - 0.2) < 1e-9 && abs($mp[2] - 1000) < 1e-3, 'move_geo relocated entity';

my $freed = $s->insert_geo(0.1, 0.1, 0, -4);
$s->remove($freed);
ok !$s->move_geo($freed, 0.2, 0.2, 0), 'move_geo returns false for a freed handle';
eval { $s->position_geo($freed) }; like $@, qr/invalid|freed/, 'position_geo croaks on a freed handle';

# ---- query_geo_radius vs brute-force 3D oracle (clustered patch) ----
my $g = Data::SpatialHash::Shared->new(undef, 6000, 0, 50000, sphere => $R);
my @pts;
for my $i (1 .. 500) {



( run in 2.020 seconds using v1.01-cache-2.11-cpan-84de2e75c66 )