4 releases (2 stable)
new 3.0.1 | Feb 13, 2025 |
---|---|
2.0.0 |
|
1.0.1 |
|
0.3.1 |
|
0.2.2 |
|
#178 in Algorithms
757 downloads per month
240KB
4.5K
SLoC
Implementation of a rolling grid. A type that stores values in a 2D (or 3D) grid and can translate the grid to a new location. This is ideal for pseudo-infinite worlds where you want a buffer to store a region of chunks that can be moved around. Moving the region (and setting new values) is O(n) where n is the number of cells that would change position during the move operation. The move operation will call a callback with the old position, the new position, and the old value and the callback is expected to return the new value. This functionality allows you to move the grid and save any cells being unloaded and load the new cell at the same time. The move operation can be thought of as a 2D (or 3D) ring buffer. If it were 1D, a move operation might look like this:
Offset: 1
old: [0, 1, 2, 3, 4]
new: [1, 2, 3, 4, 0]
As you can see, the 0 was moved to the end of the buffer. Since the 0 was moved to the end of the buffer, it needs to be reloaded, so the move operation will call reload:
reload(0, 5, Some(0))
Let's say you had this grid:
0123
4567
89AB
CDEF
If you were to translate it by the offset (1, 1)
, the result would be this grid (without modifying the values during repositioning):
5674
9AB8
DEFC
1230
Now, that's not the benefit of this library. When repositioning the grid, it doesn't move any elements at all. Instead, it keeps track of movement offsets and allows the reposition operation to be really fast because it's not actually moving anything around in memory. It calculates the offset and wrap offset (the offset where (0, 0) begins, everything before that is wrapped to the end). Then the algorithm calculates which cells should change and calls the supplied callback for each of those cells.
Here are a couple examples in practice
let mut grid = RollGrid2D::new((4, 4), (0, 0), |pos: (i32, i32)| {
pos
});
println!("Initial grid:");
print_grid(&grid);
let mut iterations = 0;
let mut changes = vec![];
grid.reposition((1, 2), |old, new, cell_mut| {
iterations += 1;
changes.push((old, new));
*cell_mut = new;
});
println!("Changes:");
for (old, new) in changes {
println!("{old:?} moved to {new:?}");
}
println!("Grid repositioned to (1, 2) with {iterations} iterations:");
print_grid(&grid);
println!("Cell at (4, 5): {:?}", grid.get_copy((4, 5)).unwrap());
println!("Cell at (0, 0): {:?}", grid.get_copy((0, 0)));
Output:
Initial grid:
[
[( 0, 0), ( 1, 0), ( 2, 0), ( 3, 0)]
[( 0, 1), ( 1, 1), ( 2, 1), ( 3, 1)]
[( 0, 2), ( 1, 2), ( 2, 2), ( 3, 2)]
[( 0, 3), ( 1, 3), ( 2, 3), ( 3, 3)]
]
Changes:
(0, 2) moved to (4, 2)
(0, 3) moved to (4, 3)
(1, 0) moved to (1, 4)
(2, 0) moved to (2, 4)
(3, 0) moved to (3, 4)
(1, 1) moved to (1, 5)
(2, 1) moved to (2, 5)
(3, 1) moved to (3, 5)
(0, 0) moved to (4, 4)
(0, 1) moved to (4, 5)
Grid repositioned to (1, 2) with 10 iterations:
[
[( 1, 2), ( 2, 2), ( 3, 2), ( 4, 2)]
[( 1, 3), ( 2, 3), ( 3, 3), ( 4, 3)]
[( 1, 4), ( 2, 4), ( 3, 4), ( 4, 4)]
[( 1, 5), ( 2, 5), ( 3, 5), ( 4, 5)]
]
Cell at (4, 5): (4, 5)
Cell at (0, 0): None
One more example, a little more advanced:
chunks.reposition((chunk_x, chunk_z), |old_pos, (x, z), chunk| {
self.unload_chunk(&mut chunk);
chunk.block_offset = Coord::new(x * 16, WORLD_BOTTOM, z * 16);
if let Some(region) = regions.get_mut((x >> 5, z >> 5)) {
let result = region.read((x & 31, z & 31), |reader| {
chunk.read_from(reader, self)
});
match result {
Err(Error::ChunkNotFound) => (/* Do nothing, that just means it's an empty chunk */),
Err(err) => {
panic!("Error: {err}");
}
_ => (),
}
chunk.edit_time = region.get_timestamp((x & 31, z & 31));
}
chunk.block_offset.x = x * 16;
chunk.block_offset.z = z * 16;
});
This reposition
method works for the 2d and 3d variants of the rollgrid.
You can modify this code to fit your purpose.
Changelog
3.0.0-alpha1
Bug Fixes
- Integer overflows in as many places as I could find.
- Major bug that caused the grids to become invalidated during
resize_and_reposition
operation.
Additions
- Added
Grid2D
andGrid3D
. - Added
math
module, for some useful math functionality that this crate uses. - Added
new_zst
constructor toRollGrid2D<()>
andRollGrid3D<()>
. - Added
new_1d
andtry_new_1d
functions toFixedArray
. - Added
into_raw
andfrom_raw
toFixedArray
. - Added
subgrid
,subgrid_mut
,copy_subgrid
, andclone_subgrid
toRollGrid2D
andRollGrid3D
. - Implemented
Send
forRollGrid2D<T>
whereT: Send
. - Implemented
Send
forRollGrid3D<T>
whereT: Send
. - Implemented
Sync
forRollGrid2D<T>
whereT: Sync
. - Implemented
Sync
forRollGrid3D<T>
whereT: Sync
. - Implemented
Clone
forFixedArray<T>
whereT: Clone
. - Implemented
Clone
forRollGrid2D<T>
whereT: Clone
. - Implemented
Clone
forRollGrid3D<T>
whereT: Clone
. - Implemented
FromIterator
forFixedArray
. - Implemented
From<Vec<T>>
forFixedArray<T>
. - Implemented
From<Box<[T]>>
forFixedArray<T>
. - Implemented
Index<(i32, i32)>
forRollGrid2D<T>
. - Implemented
IndexMut<(i32, i32)>
forRollGrid2D<T>
. - Implemented
Index<(i32, i32, i32)>
forRollGrid3D<T>
. - Implemented
IndexMut<(i32, i32, i32)>
forRollGrid3D<T>
. - Implemented
AsRef<FixedArray<T>>
forFixedArray<T>
. - Implemented
AsMut<FixedArray<T>>
forFixedArray<T>
. - Implemented
AsRef<[T]>
forFixedArray<T>
. - Implemented
AsMut<[T]>
forFixedArray<T>
. - Implemented
Borrow<[T]>
forFixedArray<T>
. - Implemented
BorrowMut<[T]>
forFixedArray<T>
.
Changes
- Modified
RollGrid2D
andRollGrid3D
to useu32
for dimensions rather thanusize
. This makes it more clear what types of values should be used. - Reset
capacity
inFixedArray::internal_dealloc
instead ofFixedArray::dealloc
. - Modified
FixedArray::into_raw
to be a static function rather than a method. relative_offset
now returns(i64, ...)
instead of(i32, ...)
.- Operations that took dimensions as individual arguments now take them as tuples.
read
inRollGrid2D
andRollGrid3D
now returnsT
instead ofOption<T>
and panics if the coordinate is out of bounds.
2.0.0
- Changed the internal representation of the cells in
RollGrid2D
andRollGrid3D
fromBox<[Option<T>]>
torollgrid::cells::FixedArray<T>
.FixedArray
is an internal type that was created to fulfill the needs of this crate. - Removed generic coordinate parameters. Coordinate arguments must be explicitly
(i32, i32)
forRollGrid2D
and(i32, i32, i32)
forRollGrid3D
. - For
reposition
functions, thereload
callback now takes&mut T
rather thanOption<T>
and returns()
instead ofOption<T>
. - For resize functions, the
manage
parameter is now a generic of the traitCellManage<C, T>
, which separates the functionality ofload
,reload
, andunload
. There is alsoTryCellManage<C, T, E>
for the fallible resize functions. - Removed
get_opt
,get_opt_mut
, andset_opt
. - Removed
get_or_insert
andget_or_insert_with
. - Removed
take
. - Changed the
Item
forRollGridXDIterator
andRollGridXDMutIterator
. Now returns&T
/&mut T
rather thanOption<&T>
/Option<&mut T>
. - Replaced the
new
constructor forRollGrid2D
andRollGrid3D
with thenew_with_init
constructor.new_with_init
is now callednew
and the originalnew
no longer exists. Likewise changed the name oftry_new_with_init
totry_new
. - In
RollGrid2D
, changed the inflate/deflate to use both width and height rather than inflating/deflating both simultaneously. This is to ensure parity withRollGrid3D
.
Dependencies
~165KB