[][src]Module building_blocks_storage::array

N-dimensional arrays, where N is 2 or 3.

The domains of all arrays are located within an ambient space, a signed integer lattice where the elements are Point2i or Point3i. This means they contain data at exactly the set of points in an ExtentN, and no more.

You can index an array with 3 kinds of coordinates, with traits:

  • Get*<Stride>: flat array offset
  • Get*<&LocalN>: N-dimensional point in extent-local coordinates (i.e. min = [0, 0, 0])
  • Get*<&PointN>: N-dimensional point in global (ambient) coordinates

Indexing assumes that the coordinates are in-bounds of the array, panicking otherwise.

Arrays also support fast iteration over extents with ForEach* trait impls. These methods will only iterate over the section of the extent which is in-bounds of the array, so it's impossible to index out of bounds.

use building_blocks_core::prelude::*;
use building_blocks_storage::prelude::*;

let array_extent = Extent3i::from_min_and_shape(PointN([0; 3]), PointN([64; 3]));
let mut array = Array3::fill(array_extent, 0);

// Write all points in the extent to the same value.
let write_extent = Extent3i::from_min_and_lub(PointN([10, 10, 10]), PointN([20, 20, 20]));
array.for_each_mut(&write_extent, |_stride: Stride, value| *value = 1);

// Only the points in the extent should have been written.
array.for_each(array.extent(), |p: Point3i, value|
    if write_extent.contains(&p) {
        assert_eq!(value, 1);
    } else {
        assert_eq!(value, 0);
    }
);

Since Stride lookups are fast and linear, they are ideal for kernel-based algorithms (like edge/surface detection). Use the ForEach*<N, Stride> traits to iterate over an extent and use the linearity of Stride to access adjacent points.

// Use a more interesting data set, just to show off this constructor.
let mut array = Array3::fill_with(extent, |p| if p.x() % 2 == 0 { 1 } else { 0 });

let subextent = Extent3i::from_min_and_shape(PointN([1; 3]), PointN([62; 3]));

// Some of these offsets include negative coordinates, which would underflow when translated
// into an unsigned index. That's OK though, because Stride is intended to be used with modular
// arithmetic.
let offsets = Local::localize_points(&Point3i::von_neumann_offsets());
let mut neighbor_strides = [Stride(0); 6];
array.strides_from_local_points(&offsets, &mut neighbor_strides);

// Sum up the values in the Von Neumann neighborhood of each point, acting as a sort of blur
// filter.
array.for_each(&subextent, |stride: Stride, value| {
    let mut neighborhood_sum = value;
    for offset in neighbor_strides.iter() {
        let adjacent_value = array.get(stride + *offset);
        neighborhood_sum += adjacent_value;
    }
});

This means you keep the performance of simple array indexing, as opposed to indexing with a Point3i, which requires 2 multiplications to convert to a Stride. You'd be surprised how important this difference can be in tight loops.

Structs

ArrayCopySrc
ArrayN

A map from lattice location PointN<N> to data T, stored as a flat array on the heap.

FastLz4

A compression algorithm that decompresses quickly, but only on the same platform where it was compressed.

FastLz4CompressedArrayN

A compressed ArrayN that decompresses quickly, but only on the same platform where it was compressed.

Local

Map-local coordinates.

Stride

The most efficient coordinates for slice-backed lattice maps. A single number that translates directly to a slice offset.

Traits

Array

When a lattice map implements Array, that means there is some underlying array with the location and shape dictated by the extent.

ArrayIndexer