made the groups sortable
This commit is contained in:
2
TODO
2
TODO
@@ -8,8 +8,6 @@
|
||||
* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects
|
||||
* allow for built-in parallel each if possible
|
||||
* allow to replace std:: with custom implementations
|
||||
* allow to sort groups (::respect can already work with begin/end instead of a whole sparse set)
|
||||
-it would ease by far the group trick for hierarchies that requires otherwise more boilerplate
|
||||
* remove runtime views, welcome reflection and what about snapshot?
|
||||
* empty components model allows for shared components and prefabs unity-like
|
||||
- each with entity return the shared component multiple times, one per entity that refers to it
|
||||
|
||||
@@ -1160,8 +1160,11 @@ auto group = registry.group<position, velocity>(entt::exclude<renderable>);
|
||||
|
||||
Once created, the group gets the ownership of all the components specified in
|
||||
the template parameter list and arranges their pools so as to iterate all of
|
||||
them as fast as possible.<br/>
|
||||
them as fast as possible.
|
||||
|
||||
Sorting owned components is no longer allowed once the group has been created.
|
||||
However, full-owning groups can be sorted by means of the `sort` member
|
||||
function, if required.
|
||||
|
||||
### Partial-owning groups
|
||||
|
||||
@@ -1186,8 +1189,11 @@ auto group = registry.group<position>(entt::get<velocity>, entt::exclude<rendera
|
||||
Once created, the group gets the ownership of all the components specified in
|
||||
the template parameter list and arranges their pools so as to iterate all of
|
||||
them as fast as possible. The ownership of the types provided via `entt::get`
|
||||
doesn't pass to the group instead.<br/>
|
||||
doesn't pass to the group instead.
|
||||
|
||||
Sorting owned components is no longer allowed once the group has been created.
|
||||
However, partial-owning groups can be sorted by means of the `sort` member
|
||||
function, if required.
|
||||
|
||||
### Non-owning groups
|
||||
|
||||
@@ -1208,9 +1214,13 @@ Filtering entities by components is also supported:
|
||||
auto group = registry.group<>(entt::get<position, velocity>, entt::exclude<renderable>);
|
||||
```
|
||||
|
||||
The group doesn't receive the ownership of any type of component in this case.
|
||||
This has a positive implication, that is, the fact that non-owning groups can be
|
||||
sorted by means of the `sort` member function, if required.
|
||||
The group doesn't receive the ownership of any type of component in this
|
||||
case. This type of groups is therefore the least performing in general, but also
|
||||
the only one that can be used in any situation to improve a performance where
|
||||
necessary.
|
||||
|
||||
Non-owning groups can be sorted by means of the `sort` member function, if
|
||||
required.
|
||||
|
||||
# Types: const, non-const and all in between
|
||||
|
||||
|
||||
@@ -666,6 +666,72 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort a group according to the given comparison function.
|
||||
*
|
||||
* Sort the group so that iterating it with a couple of iterators returns
|
||||
* entities and components in the expected order. See `begin` and `end` for
|
||||
* more details.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
* is _less_ than the second one, `false` otherwise. The signature of the
|
||||
* comparison function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* Moreover, the comparison function object shall induce a
|
||||
* _strict weak ordering_ on the values.
|
||||
*
|
||||
* The sort function oject must offer a member function template
|
||||
* `operator()` that accepts three arguments:
|
||||
*
|
||||
* * An iterator to the first element of the range to sort.
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
* `sort` has been invoked.
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
std::vector<size_type> copy(*length);
|
||||
std::iota(copy.begin(), copy.end(), 0);
|
||||
|
||||
algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare), data = data()](const auto lhs, const auto rhs) {
|
||||
return compare(data[lhs], data[rhs]);
|
||||
}, std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos = 0, last = copy.size(); pos < last; ++pos) {
|
||||
auto curr = pos;
|
||||
auto next = copy[curr];
|
||||
|
||||
while(curr != next) {
|
||||
const auto lhs = copy[curr];
|
||||
const auto rhs = copy[next];
|
||||
(std::swap(std::get<pool_type<Owned> *>(pools)->raw()[lhs], std::get<pool_type<Owned> *>(pools)->raw()[rhs]), ...);
|
||||
(std::get<pool_type<Owned> *>(pools)->swap(lhs, rhs), ...);
|
||||
copy[curr] = curr;
|
||||
curr = next;
|
||||
next = copy[curr];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const typename basic_registry<Entity>::size_type *length;
|
||||
const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <entt/entity/registry.hpp>
|
||||
#include <entt/entity/group.hpp>
|
||||
|
||||
struct empty_type {};
|
||||
struct boxed_int { int value; };
|
||||
|
||||
TEST(NonOwningGroup, Functionalities) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<>(entt::get<int, char>);
|
||||
@@ -346,7 +349,6 @@ TEST(NonOwningGroup, ExcludedComponents) {
|
||||
}
|
||||
|
||||
TEST(NonOwningGroup, EmptyAndNonEmptyTypes) {
|
||||
struct empty_type {};
|
||||
entt::registry registry;
|
||||
const auto group = registry.group<>(entt::get<int, empty_type>);
|
||||
|
||||
@@ -547,6 +549,155 @@ TEST(OwningGroup, Each) {
|
||||
ASSERT_EQ(cnt, std::size_t{0});
|
||||
}
|
||||
|
||||
TEST(OwningGroup, SortOrdered) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<boxed_int, char>();
|
||||
|
||||
entt::entity entities[5] = {
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create()
|
||||
};
|
||||
|
||||
registry.assign<boxed_int>(entities[0], 12);
|
||||
registry.assign<char>(entities[0], 'a');
|
||||
|
||||
registry.assign<boxed_int>(entities[1], 9);
|
||||
registry.assign<char>(entities[1], 'b');
|
||||
|
||||
registry.assign<boxed_int>(entities[2], 6);
|
||||
registry.assign<char>(entities[2], 'c');
|
||||
|
||||
registry.assign<boxed_int>(entities[3], 1);
|
||||
registry.assign<boxed_int>(entities[4], 2);
|
||||
|
||||
group.sort([&group](const auto lhs, const auto rhs) {
|
||||
return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(*(group.data() + 0u), entities[0]);
|
||||
ASSERT_EQ(*(group.data() + 1u), entities[1]);
|
||||
ASSERT_EQ(*(group.data() + 2u), entities[2]);
|
||||
ASSERT_EQ(*(group.data() + 3u), entities[3]);
|
||||
ASSERT_EQ(*(group.data() + 4u), entities[4]);
|
||||
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 0u)->value, 12);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 1u)->value, 9);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 2u)->value, 6);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 3u)->value, 1);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 4u)->value, 2);
|
||||
|
||||
ASSERT_EQ(*(group.raw<char>() + 0u), 'a');
|
||||
ASSERT_EQ(*(group.raw<char>() + 1u), 'b');
|
||||
ASSERT_EQ(*(group.raw<char>() + 2u), 'c');
|
||||
}
|
||||
|
||||
TEST(OwningGroup, SortReverse) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<boxed_int, char>();
|
||||
|
||||
entt::entity entities[5] = {
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create()
|
||||
};
|
||||
|
||||
registry.assign<boxed_int>(entities[0], 6);
|
||||
registry.assign<char>(entities[0], 'a');
|
||||
|
||||
registry.assign<boxed_int>(entities[1], 9);
|
||||
registry.assign<char>(entities[1], 'b');
|
||||
|
||||
registry.assign<boxed_int>(entities[2], 12);
|
||||
registry.assign<char>(entities[2], 'c');
|
||||
|
||||
registry.assign<boxed_int>(entities[3], 1);
|
||||
registry.assign<boxed_int>(entities[4], 2);
|
||||
|
||||
group.sort([&group](const auto lhs, const auto rhs) {
|
||||
return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(*(group.data() + 0u), entities[2]);
|
||||
ASSERT_EQ(*(group.data() + 1u), entities[1]);
|
||||
ASSERT_EQ(*(group.data() + 2u), entities[0]);
|
||||
ASSERT_EQ(*(group.data() + 3u), entities[3]);
|
||||
ASSERT_EQ(*(group.data() + 4u), entities[4]);
|
||||
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 0u)->value, 12);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 1u)->value, 9);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 2u)->value, 6);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 3u)->value, 1);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 4u)->value, 2);
|
||||
|
||||
ASSERT_EQ(*(group.raw<char>() + 0u), 'c');
|
||||
ASSERT_EQ(*(group.raw<char>() + 1u), 'b');
|
||||
ASSERT_EQ(*(group.raw<char>() + 2u), 'a');
|
||||
}
|
||||
|
||||
TEST(OwningGroup, SortUnordered) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<boxed_int, char>();
|
||||
|
||||
entt::entity entities[7] = {
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create(),
|
||||
registry.create()
|
||||
};
|
||||
|
||||
registry.assign<boxed_int>(entities[0], 6);
|
||||
registry.assign<char>(entities[0], 'a');
|
||||
|
||||
registry.assign<boxed_int>(entities[1], 3);
|
||||
registry.assign<char>(entities[1], 'b');
|
||||
|
||||
registry.assign<boxed_int>(entities[2], 1);
|
||||
registry.assign<char>(entities[2], 'c');
|
||||
|
||||
registry.assign<boxed_int>(entities[3], 9);
|
||||
registry.assign<char>(entities[3], 'd');
|
||||
|
||||
registry.assign<boxed_int>(entities[4], 12);
|
||||
registry.assign<char>(entities[4], 'e');
|
||||
|
||||
registry.assign<boxed_int>(entities[5], 4);
|
||||
registry.assign<boxed_int>(entities[6], 5);
|
||||
|
||||
group.sort([&group](const auto lhs, const auto rhs) {
|
||||
return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value;
|
||||
});
|
||||
|
||||
ASSERT_EQ(*(group.data() + 0u), entities[4]);
|
||||
ASSERT_EQ(*(group.data() + 1u), entities[3]);
|
||||
ASSERT_EQ(*(group.data() + 2u), entities[0]);
|
||||
ASSERT_EQ(*(group.data() + 3u), entities[1]);
|
||||
ASSERT_EQ(*(group.data() + 4u), entities[2]);
|
||||
ASSERT_EQ(*(group.data() + 5u), entities[5]);
|
||||
ASSERT_EQ(*(group.data() + 6u), entities[6]);
|
||||
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 0u)->value, 12);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 1u)->value, 9);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 2u)->value, 6);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 3u)->value, 3);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 4u)->value, 1);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 5u)->value, 4);
|
||||
ASSERT_EQ((group.raw<boxed_int>() + 6u)->value, 5);
|
||||
|
||||
ASSERT_EQ(*(group.raw<char>() + 0u), 'e');
|
||||
ASSERT_EQ(*(group.raw<char>() + 1u), 'd');
|
||||
ASSERT_EQ(*(group.raw<char>() + 2u), 'a');
|
||||
ASSERT_EQ(*(group.raw<char>() + 3u), 'b');
|
||||
ASSERT_EQ(*(group.raw<char>() + 4u), 'c');
|
||||
}
|
||||
|
||||
TEST(OwningGroup, IndexRebuiltOnDestroy) {
|
||||
entt::registry registry;
|
||||
auto group = registry.group<int>(entt::get<unsigned int>);
|
||||
@@ -704,7 +855,6 @@ TEST(OwningGroup, ExcludedComponents) {
|
||||
}
|
||||
|
||||
TEST(OwningGroup, EmptyAndNonEmptyTypes) {
|
||||
struct empty_type {};
|
||||
entt::registry registry;
|
||||
const auto group = registry.group<empty_type>(entt::get<int>);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user