zig-raylib-engine/zig-ecs/src/ecs/sparse_set.zig

342 lines
11 KiB
Zig
Raw Normal View History

2020-05-31 21:28:29 -07:00
const std = @import("std");
const warn = std.debug.warn;
2020-06-11 22:28:29 -07:00
const utils = @import("utils.zig");
2020-06-10 13:10:56 -07:00
const ReverseSliceIterator = @import("utils.zig").ReverseSliceIterator;
2020-05-31 21:28:29 -07:00
// TODO: fix entity_mask. it should come from EntityTraitsDefinition.
2020-06-04 19:19:29 -07:00
pub fn SparseSet(comptime SparseT: type) type {
2020-05-31 21:28:29 -07:00
return struct {
const Self = @This();
2020-06-04 19:19:29 -07:00
sparse: std.ArrayList(SparseT),
2020-05-31 21:28:29 -07:00
dense: std.ArrayList(SparseT),
entity_mask: SparseT,
2020-06-02 19:55:19 -07:00
allocator: ?*std.mem.Allocator,
2020-05-31 21:28:29 -07:00
2020-06-02 19:55:19 -07:00
pub fn initPtr(allocator: *std.mem.Allocator) *Self {
2020-05-31 21:28:29 -07:00
var set = allocator.create(Self) catch unreachable;
2020-06-04 19:19:29 -07:00
set.sparse = std.ArrayList(SparseT).init(allocator);
2020-05-31 21:28:29 -07:00
set.dense = std.ArrayList(SparseT).init(allocator);
set.entity_mask = std.math.maxInt(SparseT);
set.allocator = allocator;
return set;
}
2020-06-02 19:55:19 -07:00
pub fn init(allocator: *std.mem.Allocator) Self {
return Self{
2020-06-04 19:19:29 -07:00
.sparse = std.ArrayList(SparseT).init(allocator),
2020-06-02 19:55:19 -07:00
.dense = std.ArrayList(SparseT).init(allocator),
.entity_mask = std.math.maxInt(SparseT),
.allocator = null,
};
}
2020-05-31 21:28:29 -07:00
pub fn deinit(self: *Self) void {
self.dense.deinit();
self.sparse.deinit();
2020-06-02 19:55:19 -07:00
if (self.allocator) |allocator|
allocator.destroy(self);
2020-05-31 21:28:29 -07:00
}
2020-06-11 22:28:29 -07:00
pub fn page(self: Self, sparse: SparseT) usize {
2020-05-31 21:28:29 -07:00
// TODO: support paging
// return (sparse & EntityTraits.entity_mask) / sparse_per_page;
return sparse & self.entity_mask;
}
2020-06-03 20:13:16 -07:00
fn offset(self: Self, sparse: SparseT) usize {
// TODO: support paging
// return entt & (sparse_per_page - 1)
return sparse & self.entity_mask;
}
2020-06-04 19:19:29 -07:00
fn assure(self: *Self, pos: usize) []SparseT {
2020-05-31 21:28:29 -07:00
// TODO: support paging
2020-06-01 20:05:07 -07:00
if (self.sparse.capacity <= pos or self.sparse.capacity == 0) {
2020-05-31 21:28:29 -07:00
const amount = pos + 1 - self.sparse.capacity;
// expand and fill with maxInt as an identifier
const old_len = self.sparse.items.len;
self.sparse.resize(self.sparse.items.len + amount) catch unreachable;
self.sparse.expandToCapacity();
2020-06-04 19:19:29 -07:00
std.mem.set(SparseT, self.sparse.items[old_len..self.sparse.items.len], std.math.maxInt(SparseT));
2020-05-31 21:28:29 -07:00
}
return self.sparse.items;
}
2020-06-03 15:01:16 -07:00
/// Increases the capacity of a sparse sets index array
2020-05-31 21:28:29 -07:00
pub fn reserve(self: *Self, cap: usize) void {
2020-06-03 15:01:16 -07:00
self.sparse.resize(cap) catch unreachable;
2020-05-31 21:28:29 -07:00
}
2020-06-03 15:01:16 -07:00
/// Returns the number of dense elements that a sparse set has currently allocated space for
2020-05-31 21:28:29 -07:00
pub fn capacity(self: *Self) usize {
return self.dense.capacity;
}
2020-06-03 15:01:16 -07:00
/// Returns the number of dense elements in a sparse set
pub fn len(self: Self) usize {
2020-05-31 21:28:29 -07:00
return self.dense.items.len;
}
pub fn empty(self: *Self) bool {
return self.dense.items.len == 0;
}
2020-06-09 10:21:42 -07:00
pub fn data(self: Self) []const SparseT {
return self.dense.items;
}
pub fn dataPtr(self: Self) *const []SparseT {
2020-05-31 21:28:29 -07:00
return &self.dense.items;
}
pub fn contains(self: Self, sparse: SparseT) bool {
const curr = self.page(sparse);
2020-06-12 11:44:01 -07:00
if (curr >= self.sparse.items.len) {
2020-05-31 21:28:29 -07:00
return false;
2020-06-12 11:44:01 -07:00
}
2020-05-31 21:28:29 -07:00
// testing against maxInt permits to avoid accessing the packed array
2020-06-04 19:19:29 -07:00
return curr < self.sparse.items.len and self.sparse.items[curr] != std.math.maxInt(SparseT);
2020-05-31 21:28:29 -07:00
}
/// Returns the position of an entity in a sparse set
2020-06-04 19:19:29 -07:00
pub fn index(self: Self, sparse: SparseT) SparseT {
2020-05-31 21:28:29 -07:00
std.debug.assert(self.contains(sparse));
return self.sparse.items[self.offset(sparse)];
}
/// Assigns an entity to a sparse set
pub fn add(self: *Self, sparse: SparseT) void {
std.debug.assert(!self.contains(sparse));
// assure(page(entt))[offset(entt)] = packed.size()
2020-06-04 19:19:29 -07:00
self.assure(self.page(sparse))[self.offset(sparse)] = @intCast(SparseT, self.dense.items.len);
2020-05-31 21:28:29 -07:00
_ = self.dense.append(sparse) catch unreachable;
}
/// Removes an entity from a sparse set
pub fn remove(self: *Self, sparse: SparseT) void {
std.debug.assert(self.contains(sparse));
const curr = self.page(sparse);
const pos = self.offset(sparse);
const last_dense = self.dense.items[self.dense.items.len - 1];
self.dense.items[self.sparse.items[curr]] = last_dense;
self.sparse.items[self.page(last_dense)] = self.sparse.items[curr];
2020-06-04 19:19:29 -07:00
self.sparse.items[curr] = std.math.maxInt(SparseT);
2020-05-31 21:28:29 -07:00
_ = self.dense.pop();
}
2020-06-03 20:13:16 -07:00
/// Swaps two entities in the internal packed and sparse arrays
2020-05-31 21:28:29 -07:00
pub fn swap(self: *Self, sparse_l: SparseT, sparse_r: SparseT) void {
var from = &self.sparse.items[sparse_l];
var to = &self.sparse.items[sparse_r];
std.mem.swap(SparseT, &self.dense.items[from.*], &self.dense.items[to.*]);
2020-06-04 19:19:29 -07:00
std.mem.swap(SparseT, from, to);
2020-05-31 21:28:29 -07:00
}
/// Sort elements according to the given comparison function
2020-06-12 18:15:47 -07:00
pub fn sort(self: *Self, context: var, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool) void {
std.sort.insertionSort(SparseT, self.dense.items, context, lessThan);
2020-06-11 22:28:29 -07:00
2020-06-12 13:47:53 -07:00
for (self.dense.items) |sparse, i| {
2020-06-12 11:44:01 -07:00
// sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos);
2020-06-12 13:47:53 -07:00
self.sparse.items[self.dense.items[self.page(@intCast(SparseT, i))]] = @intCast(SparseT, i);
2020-06-11 22:28:29 -07:00
}
}
/// Sort elements according to the given comparison function and keeps sub_items with the same sort
2020-06-12 18:15:47 -07:00
pub fn sortSub(self: *Self, context: var, comptime lessThan: fn (@TypeOf(context), SparseT, SparseT) bool, comptime T: type, sub_items: []T) void {
std.sort.insertionSort(SparseT, self.dense.items, context, lessThan);
2020-06-03 20:13:16 -07:00
2020-06-12 18:15:47 -07:00
for (self.dense.items) |sparse, pos| {
var curr = @intCast(SparseT, pos);
var next = self.index(self.dense.items[curr]);
while (curr != next) {
std.mem.swap(T, &sub_items[self.index(self.dense.items[curr])], &sub_items[self.index(self.dense.items[next])]);
self.sparse.items[self.dense.items[self.page(@intCast(SparseT, curr))]] = @intCast(SparseT, curr);
curr = next;
next = self.index(self.dense.items[curr]);
}
2020-06-03 20:13:16 -07:00
}
2020-05-31 21:28:29 -07:00
}
2020-06-12 13:47:53 -07:00
/// flips the script and uses the sparse set as the subordinate and does the sorting on the items slice
pub fn sortSubSub(self: *Self, context: var, comptime T: type, comptime lessThan: fn (@TypeOf(context), T, T) bool, items: []T) void {
utils.sortSubSub(T, SparseT, items, self.dense.items, context, lessThan);
2020-06-12 11:44:01 -07:00
for (self.dense.items) |sparse, i| {
// sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos);
2020-06-12 13:47:53 -07:00
self.sparse.items[self.dense.items[self.page(@intCast(SparseT, i))]] = @intCast(SparseT, i);
2020-06-12 11:44:01 -07:00
}
}
2020-06-03 20:13:16 -07:00
/// Sort entities according to their order in another sparse set. Other is the master in this case.
2020-05-31 21:28:29 -07:00
pub fn respect(self: *Self, other: *Self) void {
2020-06-04 19:19:29 -07:00
var pos = @as(SparseT, 0);
var i = @as(SparseT, 0);
2020-06-03 20:13:16 -07:00
while (i < other.dense.items.len) : (i += 1) {
if (self.contains(other.dense.items[i])) {
if (other.dense.items[i] != self.dense.items[pos]) {
self.swap(self.dense.items[pos], other.dense.items[i]);
}
pos += 1;
}
}
2020-05-31 21:28:29 -07:00
}
pub fn clear(self: *Self) void {
self.sparse.items.len = 0;
self.dense.items.len = 0;
}
2020-06-10 13:10:56 -07:00
pub fn reverseIterator(self: *Self) ReverseSliceIterator(SparseT) {
return ReverseSliceIterator(SparseT).init(self.dense.items);
}
2020-05-31 21:28:29 -07:00
};
}
2020-06-03 20:13:16 -07:00
fn printSet(set: *SparseSet(u32, u8)) void {
std.debug.warn("\nsparse -----\n", .{});
for (set.sparse.items) |sparse| {
std.debug.warn("{}\t", .{sparse});
}
std.debug.warn("\ndense -----\n", .{});
for (set.dense.items) |dense| {
std.debug.warn("{}\t", .{dense});
}
std.debug.warn("\n\n", .{});
}
2020-05-31 21:28:29 -07:00
test "add/remove/clear" {
2020-06-04 19:19:29 -07:00
var set = SparseSet(u32).initPtr(std.testing.allocator);
2020-05-31 21:28:29 -07:00
defer set.deinit();
set.add(4);
set.add(3);
std.testing.expectEqual(set.len(), 2);
std.testing.expectEqual(set.index(4), 0);
std.testing.expectEqual(set.index(3), 1);
set.remove(4);
std.testing.expectEqual(set.len(), 1);
set.clear();
std.testing.expectEqual(set.len(), 0);
}
test "grow" {
2020-06-04 19:19:29 -07:00
var set = SparseSet(u32).initPtr(std.testing.allocator);
2020-05-31 21:28:29 -07:00
defer set.deinit();
var i = @as(usize, std.math.maxInt(u8));
while (i > 0) : (i -= 1) {
set.add(@intCast(u32, i));
}
std.testing.expectEqual(set.len(), std.math.maxInt(u8));
}
test "swap" {
2020-06-04 19:19:29 -07:00
var set = SparseSet(u32).initPtr(std.testing.allocator);
2020-05-31 21:28:29 -07:00
defer set.deinit();
set.add(4);
set.add(3);
std.testing.expectEqual(set.index(4), 0);
std.testing.expectEqual(set.index(3), 1);
set.swap(4, 3);
std.testing.expectEqual(set.index(3), 0);
std.testing.expectEqual(set.index(4), 1);
}
test "data() synced" {
2020-06-04 19:19:29 -07:00
var set = SparseSet(u32).initPtr(std.testing.allocator);
2020-05-31 21:28:29 -07:00
defer set.deinit();
set.add(0);
set.add(1);
set.add(2);
set.add(3);
var data = set.data();
2020-06-09 10:21:42 -07:00
std.testing.expectEqual(data[1], 1);
2020-05-31 21:28:29 -07:00
std.testing.expectEqual(set.len(), data.len);
set.remove(0);
set.remove(1);
2020-06-09 10:21:42 -07:00
std.testing.expectEqual(set.len(), set.data().len);
2020-05-31 21:28:29 -07:00
}
2020-06-03 20:13:16 -07:00
2020-06-10 13:10:56 -07:00
test "iterate" {
var set = SparseSet(u32).initPtr(std.testing.allocator);
defer set.deinit();
set.add(0);
set.add(1);
set.add(2);
set.add(3);
2020-06-11 22:28:29 -07:00
var i: u32 = @intCast(u32, set.len()) - 1;
2020-06-10 13:10:56 -07:00
var iter = set.reverseIterator();
while (iter.next()) |entity| {
std.testing.expectEqual(i, entity);
if (i > 0) i -= 1;
}
}
test "respect 1" {
2020-06-04 19:19:29 -07:00
var set1 = SparseSet(u32).initPtr(std.testing.allocator);
2020-06-03 20:13:16 -07:00
defer set1.deinit();
2020-06-04 19:19:29 -07:00
var set2 = SparseSet(u32).initPtr(std.testing.allocator);
2020-06-03 20:13:16 -07:00
defer set2.deinit();
set1.add(3);
set1.add(4);
set1.add(5);
set1.add(6);
set1.add(7);
set2.add(8);
set2.add(6);
set2.add(4);
set1.respect(set2);
std.testing.expectEqual(set1.dense.items[0], set2.dense.items[1]);
std.testing.expectEqual(set1.dense.items[1], set2.dense.items[2]);
}
2020-06-11 22:28:29 -07:00
const desc_u32 = std.sort.desc(u32);
2020-06-10 13:10:56 -07:00
test "respect 2" {
2020-06-04 19:19:29 -07:00
var set = SparseSet(u32).initPtr(std.testing.allocator);
2020-06-03 20:13:16 -07:00
defer set.deinit();
set.add(5);
set.add(2);
set.add(4);
set.add(1);
set.add(3);
2020-06-12 18:15:47 -07:00
set.sort({}, desc_u32);
2020-06-03 20:13:16 -07:00
for (set.dense.items) |item, i| {
if (i < set.dense.items.len - 1) {
std.debug.assert(item > set.dense.items[i + 1]);
}
}
2020-06-11 22:28:29 -07:00
}