Data-SpatialHash-Shared

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

    query region; smaller cells reduce false positives at the cost of more
    bucket lookups.

  2D vs 3D
    All methods that accept coordinates accept either (x, y) or (x, y, z).
    When z is omitted it is treated as 0, so a 2D query matches the z=0 cell
    layer (points within one "cell_size" of the z=0 plane), not an infinite
    z-column, and z does not enter the distance for a 2D call. A handle
    created with a 2D insert can be queried with either 2D or 3D calls.

  Toroidal space
    By default the grid is unbounded and Euclidean. Construct with "wrap =>
    [$Wx, $Wy]" (2D) or "[$Wx, $Wy, $Wz]" (3D) to make space a seamless
    torus: neighbour-cell expansion wraps around the grid edges and
    "query_radius", "query_knn", "each_in_radius", "query_radius_many", and
    the pair emitters all use the minimum-image (shortest wrapping) distance

        dx = abs(ax - bx);  dx = $Wx - dx if dx > $Wx / 2;   # per axis

    so an entry near 0 and one near $Wx are neighbours. Keep positions
    within "[0, $W)" per axis for the metric to be meaningful. Each wrapped
    extent must be a positive multiple of "cell_size" so the cells tile the
    world exactly, otherwise the constructor croaks. "query_cell" resolves
    its coordinate to a wrapped cell, but "query_aabb" uses the literal
    (non-wrapping) box even in a wrapping world. The wrap configuration is
    part of the mapped format and is restored on reopen; the "world"
    accessor returns the extents.

METHODS
  Constructors
        my $s = Data::SpatialHash::Shared->new($path, $max, $buckets, $cell);
        my $s = Data::SpatialHash::Shared->new(undef, $max, $buckets, $cell);
        my $s = Data::SpatialHash::Shared->new_memfd($name, $max, $buckets, $cell);
        my $s = Data::SpatialHash::Shared->new_from_fd($fd);
        my $s = Data::SpatialHash::Shared->new($path, $max, $buckets, $cell, wrap => [$Wx, $Wy]);

    $path is the backing file path; "undef" creates an anonymous mapping.
    $max is the maximum number of entries. $buckets is the bucket count (0 =
    auto). $cell is the cell size (float).

    Pass "wrap => [$Wx, $Wy]" (or "[$Wx, $Wy, $Wz]") to make the world a
    seamless torus of those extents (see "Toroidal space"); omit it for an
    unbounded Euclidean space.

    When reopening an existing backing file or memfd, the stored header
    wins: the caller's $max, $buckets, $cell, "wrap", and "sphere" arguments
    are ignored and the file's original values are used. They are still
    validated for well-formedness first (for example a non-positive $cell
    croaks), so pass plausible values even on a reopen; only the stored
    geometry is authoritative.

    "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
    nothing.

    Each entry may carry an interaction radius (default 0; must be finite
    and non-negative), used by "each_colliding_pair". Set it with the
    5-argument "insert" or with "set_radius" (which croaks on an invalid or
    freed handle); for a 2D entry with a radius, insert then call
    "set_radius". "insert_many" and "move_many" apply a whole batch under a
    single lock acquisition -- each row is an arrayref. "insert_many"
    inserts 2D entries (rows "[x,y,value]" or "[x,y,value,radius]"; use
    "insert" in a loop for 3D) and returns the list of handles, with "undef"
    for any row that overflowed the pool, was malformed (not an arrayref of
    length 3 or 4), or carried a negative or non-finite radius. "move_many"
    takes "[handle,x,y]" or "[handle,x,y,z]" rows and returns the count
    successfully moved; freed/invalid handles and malformed rows are
    skipped.

    Handles are entry slot indices starting at 0, and 0 is false in Perl.
    The very first insert into a fresh hash returns handle 0. Always test
    the result with "defined $h", never for truthiness:

        my $h = $s->insert($x, $y, $v);
        die "full" unless defined $h;   # correct: handle 0 is valid
        # WRONG: "unless $h" would treat the first handle (0) as failure

  Accessors
        $s->has($h);              # true if handle is live
        $s->value($h);            # stored value
        $s->get_radius($h);       # stored interaction radius (0 if unset)
        my ($x, $y, $z) = $s->position($h);   # current position

    "has" is the safe predicate for a possibly-freed handle; "value",
    "position", and "get_radius" croak on an invalid or freed handle.

  Queries
    Most query methods return a list of stored values (not handles) for
    matching entries; "query_radius_many" instead returns an arrayref of
    id-list arrayrefs (see "Batched radius queries"). For "query_knn",
    results are in nearest-first order.

        my @ids = $s->query_radius(x, y, r);         # 2D radius search
        my @ids = $s->query_radius(x, y, z, r);      # 3D radius search
        my @ids = $s->query_aabb(x0, y0, x1, y1);    # 2D axis-aligned box
        my @ids = $s->query_aabb(x0, y0, z0, x1, y1, z1);  # 3D box
        my @ids = $s->query_cell(x, y);              # single cell (2D)
        my @ids = $s->query_cell(x, y, z);           # single cell (3D)
        my @ids = $s->query_knn(x, y, k);            # k nearest (2D)
        my @ids = $s->query_knn(x, y, z, k);         # k nearest (3D)
        $s->each_in_radius(x, y, r, sub { my ($v) = @_; ... });    # 2D cb
        $s->each_in_radius(x, y, z, r, sub { my ($v) = @_; ... }); # 3D cb
        my $lists = $s->query_radius_many([ [x,y,r], [x,y,z,r] ]); # N radius queries, one lock



( run in 1.811 second using v1.01-cache-2.11-cpan-84de2e75c66 )