Module building_blocks_storage::array [−][src]
Expand description
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.
Indexing
You can index an array with 3 kinds of coordinates, with Get
traits:
Get*<Stride>
: flat array offsetGet*<Local<N>>
: N-dimensional point in extent-local coordinates (i.e. min =[0, 0, 0]
)Get*<PointN<N>>
: N-dimensional point in global (ambient) coordinates
Indexing assumes that the coordinates are in-bounds of the array, panicking otherwise. Bounds checking is only enabled in debug mode.
Iteration
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(Point3i::ZERO, Point3i::fill(64)); let mut array = Array3x1::fill(array_extent, 0); // Write all points in the extent to the same value. let write_extent = Extent3i::from_min_and_lub(Point3i::fill(10), Point3i::fill(20)); array.for_each_mut(&write_extent, |_: (), 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); } );
Strides
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 = Array3x1::fill_with(extent, |p| if p.x() % 2 == 0 { 1 } else { 0 }); let subextent = Extent3i::from_min_and_shape(Point3i::fill(1), Point3i::fill(62)); // 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_array(&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.
Storage
By default, Array
uses a Vec
to store elements. But any type that implements Deref<Target = [T]>
or DerefMut<Target = [T]>
should be usable. This means you can construct an array with most pointer types.
// Borrow `array`'s values for the lifetime of `other_array`. let array = Array3x1::fill(extent, 1); let other_array = Array3x1::new_one_channel(extent, array.channels().store().as_slice()); assert_eq!(other_array.get(Stride(0)), 1); // A stack-allocated array. let mut data = [1; 32 * 32 * 32]; let mut stack_array = Array3x1::new_one_channel(extent, &mut data[..]); *stack_array.get_mut(Stride(0)) = 2; assert_eq!(data[0], 2); // A boxed array. let data: Box<[u32]> = Box::new([1; 32 * 32 * 32]); // must forget the size let box_array = Array3x1::new_one_channel(extent, data); box_array.for_each(&extent, |p: Point3i, value| assert_eq!(value, 1));
Multichannel
It’s often the case that you have multiple data types to store per spatial dimension. For example, you might store geometry
data like Sd8
as well as a voxel type identifier. While you can put these in a struct, that may not be the most efficient
option. If you only need access to one of those fields of the struct for a particular algorithm, then you will needlessly
load the entire struct into cache. To avoid this problem, Array
supports storing multiple data “channels” in
structure-of-arrays (SoA) style.
#[derive(Clone, Copy, Debug, Eq, PartialEq)] struct VoxelId(u8); // This means 3D with 2 channels. Although we pass in a tuple, the two data // types are internally stored in separate arrays. let mut array = Array3x2::fill(extent, (VoxelId(0), 1.0)); // This array supports all of the usual access traits and maps the channels // to tuples as you would expect. let p = Point3i::fill(1); assert_eq!(array.get(p), (VoxelId(0), 1.0)); assert_eq!(array.get_ref(p), (&VoxelId(0), &1.0)); assert_eq!(array.get_mut(p), (&mut VoxelId(0), &mut 1.0)); // Here we choose to access just one channel, and there is no performance penalty. array.borrow_channels_mut(|(_id, dist)| dist).for_each_mut(&extent, |p: Point3i, dist| { let r = p.dot(p); *dist = (r as f32).sqrt(); }); // And if we want to copy just one of those channels into another map, we can // create a new array that borrows just one of the channels. let mut dst = Array3x1::fill(extent, 0.0); let src = array.borrow_channels(|(_id, dist)| dist); copy_extent(&extent, &src, &mut dst);
Re-exports
pub use channels::*; | |
pub use compression::*; | |
pub use multichannel_aliases::*; |
Modules
channels | |
compression | |
multichannel_aliases |
Structs
Array | A map from lattice location |
ArrayForEach | All information required to do strided iteration over an extent of a single array. |
ChunkUnits | A newtype wrapper for |
Local | Map-local coordinates. |
LockStepArrayForEach | All information required to do strided iteration over two arrays in lock step. |
Stride | The most efficient coordinates for slice-backed lattice maps. A single number that translates directly to a slice offset. |
Traits
ArrayIndexer | |
IndexedArray | When a lattice map implements |
Type Definitions
Array2ForEach | A 2D |
Array3ForEach | A 3D |
ArrayNx1 | |
ArrayNx2 | |
ArrayNx3 | |
ArrayNx4 | |
ArrayNx5 | |
ArrayNx6 | |
ChunkUnits2 | |
ChunkUnits3 | |
Local2i | Map-local coordinates, wrapping a |
Local3i | Map-local coordinates, wrapping a |
LockStepArrayForEach2 | |
LockStepArrayForEach3 |