basic group sorting
This commit is contained in:
parent
c8c58255f6
commit
e098c99632
@ -8,6 +8,8 @@ pub const Entity = @import("ecs/registry.zig").Entity;
|
||||
pub const Registry = @import("ecs/registry.zig").Registry;
|
||||
pub const BasicView = @import("ecs/views.zig").BasicView;
|
||||
pub const BasicMultiView = @import("ecs/views.zig").BasicMultiView;
|
||||
pub const BasicGroup = @import("ecs/groups.zig").BasicGroup;
|
||||
pub const OwningGroup = @import("ecs/groups.zig").OwningGroup;
|
||||
|
||||
// signals
|
||||
pub const Signal = @import("signals/signal.zig").Signal;
|
||||
|
@ -162,9 +162,11 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
|
||||
|
||||
pub usingnamespace if (is_empty_struct)
|
||||
struct {
|
||||
/// Sort Entities according to the given comparison function
|
||||
pub fn sort(self: Self, comptime sortFn: fn (void, Entity, Entity) bool) void {
|
||||
self.set.sort(sortFn);
|
||||
/// Sort Entities according to the given comparison function. Only T == Entity is allowed. The constraint param only exists for
|
||||
/// parity with non-empty Components
|
||||
pub fn sort(self: Self, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
|
||||
std.debug.assert(T == Entity);
|
||||
self.set.sort(context, lessThan);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -200,16 +202,12 @@ pub fn ComponentStorage(comptime Component: type, comptime Entity: type) type {
|
||||
}
|
||||
|
||||
/// Sort Entities or Components according to the given comparison function
|
||||
pub fn sort(self: *Self, comptime T: type, comptime lessThan: fn (void, T, T) bool) void {
|
||||
pub fn sort(self: Self, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
|
||||
std.debug.assert(T == Entity or T == Component);
|
||||
if (T == Entity) {
|
||||
self.set.sortSub(lessThan, Component, self.instances.items);
|
||||
self.set.sortSub(context, lessThan, Component, self.instances.items);
|
||||
} else if (T == Component) {
|
||||
self.set.sortSubSub({}, Component, lessThan, self.instances.items);
|
||||
// fn sorter(self: Self, a: T, b: T, sortFn) bool {
|
||||
// return sortFn(self.instances[a], self.instances[b]);
|
||||
// }
|
||||
//return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)]));
|
||||
self.set.sortSubSub(context, Component, lessThan, self.instances.items);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -339,13 +337,13 @@ test "sort empty component" {
|
||||
store.add(0, Empty{});
|
||||
|
||||
comptime const asc_u32 = std.sort.asc(u32);
|
||||
store.sort(asc_u32);
|
||||
store.sort(u32, {}, asc_u32);
|
||||
for (store.data()) |e, i| {
|
||||
std.testing.expectEqual(@intCast(u32, i), e);
|
||||
}
|
||||
|
||||
comptime const desc_u32 = std.sort.desc(u32);
|
||||
store.sort(desc_u32);
|
||||
store.sort(u32, {}, desc_u32);
|
||||
var counter: u32 = 2;
|
||||
for (store.data()) |e, i| {
|
||||
std.testing.expectEqual(counter, e);
|
||||
@ -353,7 +351,7 @@ test "sort empty component" {
|
||||
}
|
||||
}
|
||||
|
||||
test "sort component" {
|
||||
test "sort by entity" {
|
||||
std.debug.warn("\n", .{});
|
||||
|
||||
var store = ComponentStorage(f32, u32).initPtr(std.testing.allocator);
|
||||
@ -363,8 +361,37 @@ test "sort component" {
|
||||
store.add(11, @as(f32, 1.1));
|
||||
store.add(33, @as(f32, 3.3));
|
||||
|
||||
comptime const desc_u32 = std.sort.desc(f32);
|
||||
store.sort(f32, desc_u32);
|
||||
const SortContext = struct{
|
||||
store: *ComponentStorage(f32, u32),
|
||||
|
||||
fn sort(this: @This(), a: u32, b: u32) bool {
|
||||
const real_a = this.store.getConst(a);
|
||||
const real_b = this.store.getConst(b);
|
||||
return real_a > real_b;
|
||||
}
|
||||
};
|
||||
const context = SortContext{.store = store};
|
||||
store.sort(u32, context, SortContext.sort);
|
||||
|
||||
var compare: f32 = 5;
|
||||
for (store.raw()) |val, i| {
|
||||
std.testing.expect(compare > val);
|
||||
compare = val;
|
||||
}
|
||||
}
|
||||
|
||||
test "sort by component" {
|
||||
std.debug.warn("\n", .{});
|
||||
|
||||
var store = ComponentStorage(f32, u32).initPtr(std.testing.allocator);
|
||||
defer store.deinit();
|
||||
|
||||
store.add(22, @as(f32, 2.2));
|
||||
store.add(11, @as(f32, 1.1));
|
||||
store.add(33, @as(f32, 3.3));
|
||||
|
||||
comptime const desc_f32 = std.sort.desc(f32);
|
||||
store.sort(f32, {}, desc_f32);
|
||||
|
||||
var compare: f32 = 5;
|
||||
for (store.raw()) |val, i| {
|
||||
|
@ -9,40 +9,59 @@ const Entity = @import("registry.zig").Entity;
|
||||
/// BasicGroups do not own any components. Internally, they keep a SparseSet that is always kept up-to-date with the matching
|
||||
/// entities.
|
||||
pub const BasicGroup = struct {
|
||||
const Self = @This();
|
||||
|
||||
registry: *Registry,
|
||||
group_data: *Registry.GroupData,
|
||||
|
||||
pub fn init(registry: *Registry, group_data: *Registry.GroupData) Self {
|
||||
return Self{
|
||||
pub fn init(registry: *Registry, group_data: *Registry.GroupData) BasicGroup {
|
||||
return .{
|
||||
.registry = registry,
|
||||
.group_data = group_data,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn len(self: Self) usize {
|
||||
pub fn len(self: BasicGroup) usize {
|
||||
return self.group_data.entity_set.len();
|
||||
}
|
||||
|
||||
/// Direct access to the array of entities
|
||||
pub fn data(self: Self) []const Entity {
|
||||
pub fn data(self: BasicGroup) []const Entity {
|
||||
return self.group_data.entity_set.data();
|
||||
}
|
||||
|
||||
pub fn get(self: *Self, comptime T: type, entity: Entity) *T {
|
||||
pub fn get(self: *BasicGroup, comptime T: type, entity: Entity) *T {
|
||||
return self.registry.assure(T).get(entity);
|
||||
}
|
||||
|
||||
pub fn getConst(self: *Self, comptime T: type, entity: Entity) T {
|
||||
pub fn getConst(self: *BasicGroup, comptime T: type, entity: Entity) T {
|
||||
return self.registry.assure(T).getConst(entity);
|
||||
}
|
||||
|
||||
/// iterates the matched entities backwards, so the current entity can always be removed safely
|
||||
/// and newly added entities wont affect it.
|
||||
pub fn iterator(self: Self) utils.ReverseSliceIterator(Entity) {
|
||||
pub fn iterator(self: BasicGroup) utils.ReverseSliceIterator(Entity) {
|
||||
return self.group_data.entity_set.reverseIterator();
|
||||
}
|
||||
|
||||
pub fn sort(self: *BasicGroup, comptime T: type, context: var, comptime lessThan: fn (@TypeOf(context), T, T) bool) void {
|
||||
if (T == Entity) {
|
||||
self.group_data.entity_set.sort(context, lessThan);
|
||||
} else {
|
||||
// TODO: in debug mode, validate that T is present in the group
|
||||
const SortContext = struct{
|
||||
group: *BasicGroup,
|
||||
wrapped_context: @TypeOf(context),
|
||||
lessThan: fn (@TypeOf(context), T, T) bool,
|
||||
|
||||
fn sort(this: @This(), a: Entity, b: Entity) bool {
|
||||
const real_a = this.group.getConst(T, a);
|
||||
const real_b = this.group.getConst(T, b);
|
||||
return this.lessThan(this.wrapped_context, real_a, real_b);
|
||||
}
|
||||
};
|
||||
var wrapper = SortContext{.group = self, .wrapped_context = context, .lessThan = lessThan};
|
||||
self.group_data.entity_set.sort(wrapper, SortContext.sort);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const OwningGroup = struct {
|
||||
|
@ -412,10 +412,10 @@ pub const Registry = struct {
|
||||
return self.singletons;
|
||||
}
|
||||
|
||||
pub fn sort(self: *Registry, comptime T: type) void {
|
||||
pub fn sort(self: *Registry, comptime T: type, comptime lessThan: fn (void, T, T) bool) void {
|
||||
const comp = self.assure(T);
|
||||
std.debug.assert(comp.super == 0);
|
||||
unreachable;
|
||||
comp.sort(T, lessThan);
|
||||
}
|
||||
|
||||
/// Checks whether the given component belongs to any group. If so, it is not sortable directly.
|
||||
@ -563,7 +563,8 @@ pub const Registry = struct {
|
||||
new_group_data.entity_set.add(entity);
|
||||
}
|
||||
} else {
|
||||
// ??? why not? we cannot iterate backwards because we want to leave behind valid entities in case of owned types
|
||||
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
|
||||
// ??? why not?
|
||||
var first_owned_storage = self.assure(owned[0]);
|
||||
for (first_owned_storage.data()) |entity| {
|
||||
new_group_data.maybeValidIf(entity);
|
||||
|
@ -143,8 +143,8 @@ pub fn SparseSet(comptime SparseT: type) type {
|
||||
}
|
||||
|
||||
/// Sort elements according to the given comparison function
|
||||
pub fn sort(self: *Self, comptime sortFn: fn (void, SparseT, SparseT) bool) void {
|
||||
std.sort.insertionSort(SparseT, self.dense.items, {}, sortFn);
|
||||
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);
|
||||
|
||||
for (self.dense.items) |sparse, i| {
|
||||
// sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos);
|
||||
@ -153,12 +153,20 @@ pub fn SparseSet(comptime SparseT: type) type {
|
||||
}
|
||||
|
||||
/// Sort elements according to the given comparison function and keeps sub_items with the same sort
|
||||
pub fn sortSub(self: *Self, comptime sortFn: fn (void, SparseT, SparseT) bool, comptime T: type, sub_items: []T) void {
|
||||
utils.sortSub(SparseT, T, self.dense.items, sub_items, sortFn);
|
||||
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);
|
||||
|
||||
for (self.dense.items) |sparse, i| {
|
||||
// sparse[page(packed[pos])][offset(packed[pos])] = entity_type(pos);
|
||||
self.sparse.items[self.dense.items[self.page(@intCast(SparseT, i))]] = @intCast(SparseT, i);
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,7 +331,7 @@ test "respect 2" {
|
||||
set.add(1);
|
||||
set.add(3);
|
||||
|
||||
set.sort(desc_u32);
|
||||
set.sort({}, desc_u32);
|
||||
|
||||
for (set.dense.items) |item, i| {
|
||||
if (i < set.dense.items.len - 1) {
|
||||
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||
const warn = std.debug.warn;
|
||||
const ecs = @import("ecs");
|
||||
const Registry = @import("ecs").Registry;
|
||||
const BasicGroup = @import("ecs").BasicGroup;
|
||||
|
||||
const Velocity = struct { x: f32 = 0, y: f32 = 0 };
|
||||
const Position = struct { x: f32 = 0, y: f32 = 0 };
|
||||
@ -14,29 +15,77 @@ const Rotation = struct { x: f32 = 0 };
|
||||
fn printStore(store: var, name: []const u8) void {
|
||||
warn("--- {} ---\n", .{name});
|
||||
for (store.set.dense.items) |e, i| {
|
||||
warn("e[{}] s[{}]{}", .{e, store.set.page(store.set.dense.items[i]), store.set.sparse.items[store.set.page(store.set.dense.items[i])]});
|
||||
warn("e[{}] s[{}]{}", .{ e, store.set.page(store.set.dense.items[i]), store.set.sparse.items[store.set.page(store.set.dense.items[i])] });
|
||||
warn(" ({d:.2}) ", .{store.instances.items[i]});
|
||||
}
|
||||
warn("\n", .{});
|
||||
}
|
||||
|
||||
test "sort BasicGroup by Entity" {
|
||||
var reg = Registry.init(std.testing.allocator);
|
||||
defer reg.deinit();
|
||||
|
||||
test "sort component" {
|
||||
var store = ecs.ComponentStorage(f32, u32).initPtr(std.testing.allocator);
|
||||
defer store.deinit();
|
||||
var group = reg.group(.{}, .{Sprite, Renderable}, .{});
|
||||
|
||||
store.add(22, @as(f32, 2.2));
|
||||
store.add(11, @as(f32, 1.1));
|
||||
store.add(33, @as(f32, 3.3));
|
||||
|
||||
comptime const desc_u32 = std.sort.desc(f32);
|
||||
store.sort(f32, desc_u32);
|
||||
|
||||
var compare: f32 = 5;
|
||||
for (store.raw()) |val, i| {
|
||||
std.testing.expect(compare > val);
|
||||
compare = val;
|
||||
var i: usize = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
var e = reg.create();
|
||||
reg.add(e, Sprite{ .x = @intToFloat(f32, i) });
|
||||
reg.add(e, Renderable{ .x = @intToFloat(f32, i) });
|
||||
}
|
||||
|
||||
const SortContext = struct{
|
||||
group: BasicGroup,
|
||||
|
||||
fn sort(this: *@This(), a: ecs.Entity, b: ecs.Entity) bool {
|
||||
const real_a = this.group.getConst(Sprite, a);
|
||||
const real_b = this.group.getConst(Sprite, b);
|
||||
return real_a.x > real_b.x;
|
||||
}
|
||||
};
|
||||
|
||||
var context = SortContext{.group = group};
|
||||
group.sort(ecs.Entity, &context, SortContext.sort);
|
||||
|
||||
var val: f32 = 0;
|
||||
var iter = group.iterator();
|
||||
while (iter.next()) |entity| {
|
||||
std.testing.expectEqual(val, group.getConst(Sprite, entity).x);
|
||||
val += 1;
|
||||
}
|
||||
}
|
||||
|
||||
test "sort BasicGroup by Component" {
|
||||
var reg = Registry.init(std.testing.allocator);
|
||||
defer reg.deinit();
|
||||
|
||||
var group = reg.group(.{}, .{Sprite, Renderable}, .{});
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < 5) : (i += 1) {
|
||||
var e = reg.create();
|
||||
reg.add(e, Sprite{ .x = @intToFloat(f32, i) });
|
||||
reg.add(e, Renderable{ .x = @intToFloat(f32, i) });
|
||||
}
|
||||
|
||||
const SortContext = struct{
|
||||
fn sort(this: void, a: Sprite, b: Sprite) bool {
|
||||
return a.x > b.x;
|
||||
}
|
||||
};
|
||||
group.sort(Sprite, {}, SortContext.sort);
|
||||
|
||||
var val: f32 = 0;
|
||||
var iter = group.iterator();
|
||||
while (iter.next()) |entity| {
|
||||
std.testing.expectEqual(val, group.getConst(Sprite, entity).x);
|
||||
val += 1;
|
||||
}
|
||||
}
|
||||
|
||||
test "sort OwningGroup by Entity" {
|
||||
var reg = Registry.init(std.testing.allocator);
|
||||
defer reg.deinit();
|
||||
}
|
||||
|
||||
test "nested OwningGroups add/remove components" {
|
||||
@ -87,7 +136,7 @@ test "nested OwningGroups entity order" {
|
||||
var transform_store = reg.assure(Transform);
|
||||
// printStore(sprite_store, "Sprite");
|
||||
|
||||
reg.add(1, Transform{.x = 1});
|
||||
reg.add(1, Transform{ .x = 1 });
|
||||
|
||||
// printStore(sprite_store, "Sprite");
|
||||
// printStore(transform_store, "Transform");
|
||||
|
Loading…
x
Reference in New Issue
Block a user