sort allows to work with entities

This commit is contained in:
Michele Caini
2019-04-05 23:00:58 +02:00
parent 2018163107
commit 96f267cbfd
5 changed files with 119 additions and 102 deletions

2
TODO
View File

@@ -16,11 +16,11 @@
- each components only return actual component, so shared components are returned only once
* types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there
* add take functionality, eg registry.take(entity, other); where it takes the entity and all its components from registry and move them to other
* add entity function to views/groups (component -> owner, see sparse sets)
* add opaque input iterators to views and groups that return tuples <entity, T &...> (proxy), multi-pass guaranteed
* add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes
* review sparse set to allow customization (mix pack in the spec, base is position only)
- non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array
* review 64 bit id: user defined area + dedicated member on the registry to set it
* add NIO to the EnTT in Action list (link to my CV/LinkedIN)
* remove entity function from sparse sets (no longer required)
* reactive systems

View File

@@ -338,14 +338,10 @@ To be notified when components are destroyed, use the `destruction` member
function instead.
The function type of a listener is the same in both the cases and should be
equivalent to:
equivalent to the following:
```cpp
// when the default entity and the default registry are used
void(registry &, entt::entity);
// when a different specialization of the registry is used
void(basic_registry<Entity> &, Entity);
```
In other terms, a listener is provided with the registry that triggered the
@@ -431,7 +427,7 @@ It goes without saying that sorting entities and components is possible with
`EnTT`.<br/>
In fact, there are two functions that respond to slightly different needs:
* Components can be sorted directly:
* Components can be sorted either directly:
```cpp
registry.sort<renderable>([](const auto &lhs, const auto &rhs) {
@@ -440,11 +436,20 @@ In fact, there are two functions that respond to slightly different needs:
});
```
Or by accessing their entities:
```cpp
registry.sort<renderable>([](const entt::entity &lhs, const entt::entity &rhs) {
return lhs.z < rhs.z;
});
```
There exists also the possibility to use a custom sort function object, as
long as it adheres to the requirements described in the inline
documentation.<br/>
This is possible mainly because users can get much more with a custom sort
function object if the pattern of usage is known. As an example, in case of an
function object if the usage pattern is known. As an example, in case of an
almost sorted pool, quick sort could be much, much slower than insertion sort.
* Components can be sorted according to the order imposed by another component:
@@ -594,7 +599,7 @@ temporary object.
Example of use:
```cpp
entt::continuous_loader<entity_type> loader{registry};
entt::continuous_loader<entt::entity> loader{registry};
input_archive input;
loader.entities(input)
@@ -618,7 +623,7 @@ one.
The `component` member function restores all and only the components specified
and assigns them to the right entities.<br/>
In case the component contains entities itself (either as data members of type
`entity_type` or as containers of entities), the loader can update them
`entt::entity` or as containers of entities), the loader can update them
automatically. To do that, it's enough to specify the data members to update as
shown in the example.
@@ -642,18 +647,18 @@ In particular:
function call operator with the following signature to store entities:
```cpp
void operator()(Entity);
void operator()(entt::entity);
```
Where `Entity` is the type of the entities used by the registry. Note that all
the member functions of the snapshot class make also an initial call to this
endpoint to save the _size_ of the set they are going to store.<br/>
Where `entt::entity` is the type of the entities used by the registry. Note
that all the member functions of the snapshot class make also an initial call
to this endpoint to save the _size_ of the set they are going to store.<br/>
In addition, an archive must accept a pair of entity and component for each
type to be serialized. Therefore, given a type `T`, the archive must contain a
function call operator with the following signature:
```cpp
void operator()(Entity, const T &);
void operator()(entt::entity, const T &);
```
The output archive can freely decide how to serialize the data. The register
@@ -663,11 +668,11 @@ In particular:
function call operator with the following signature to load entities:
```cpp
void operator()(Entity &);
void operator()(entt::entity &);
```
Where `Entity` is the type of the entities used by the registry. Each time the
function is invoked, the archive must read the next element from the
Where `entt::entity` is the type of the entities used by the registry. Each
time the function is invoked, the archive must read the next element from the
underlying storage and copy it in the given variable. Note that all the member
functions of a loader class make also an initial call to this endpoint to read
the _size_ of the set they are going to load.<br/>
@@ -676,7 +681,7 @@ In particular:
function call operator with the following signature:
```cpp
void operator()(Entity &, T &);
void operator()(entt::entity &, T &);
```
Every time such an operator is invoked, the archive must read the next
@@ -822,7 +827,7 @@ other than defining the null entity itself. However, there exist implicit
conversions from the null entity to identifiers of any allowed type:
```cpp
typename entt::registry::entity_type null = entt::null;
entt::entity null = entt::null;
```
Similarly, the null entity can be compared to any other identifier:
@@ -1309,7 +1314,7 @@ also references will continue to be valid.<br/>
Consider the following example:
```cpp
registry.view<position>([&](auto entity, auto &pos) {
registry.view<position>([&](const auto entity, auto &pos) {
registry.assign<position>(registry.create(), 0., 0.);
pos.x = 0.; // warning: dangling pointer
});

View File

@@ -1003,13 +1003,13 @@ public:
* @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 sort A valid sort function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort sort = Sort{}, Args &&... args) {
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!owned<Component>());
assure<Component>()->sort(std::move(compare), std::move(sort), std::forward<Args>(args)...);
assure<Component>()->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
/**

View File

@@ -1075,9 +1075,10 @@ public:
*
* 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:
* comparison function should be equivalent to one of the following:
*
* @code{.cpp}
* bool(const Entity, const Entity);
* bool(const Type &, const Type &);
* @endcode
*
@@ -1096,8 +1097,8 @@ public:
* this member function.
*
* @note
* Empty components aren't explicitly instantiated. Therefore, this function
* isn't available for them.
* Empty components aren't explicitly instantiated. Therefore, the
* comparison function must necessarily accept entity identifiers.
*
* @note
* Attempting to iterate elements using a raw pointer returned by a call to
@@ -1108,19 +1109,25 @@ public:
* @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 sort A valid sort 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 sort = Sort{}, Args &&... args) {
static_assert(!std::is_empty_v<object_type>);
inline void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
std::vector<size_type> copy(instances.size());
std::iota(copy.begin(), copy.end(), 0);
sort(copy.begin(), copy.end(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(instances[rhs]), std::as_const(instances[lhs]));
}, std::forward<Args>(args)...);
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
static_assert(!std::is_empty_v<object_type>);
algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(instances[lhs]), std::as_const(instances[rhs]));
}, std::forward<Args>(args)...);
} else {
algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare), entities = underlying_type::data()](const auto lhs, const auto rhs) {
return compare(entities[lhs], entities[rhs]);
}, std::forward<Args>(args)...);
}
for(size_type pos = 0, last = copy.size(); pos < last; ++pos) {
auto curr = pos;
@@ -1129,7 +1136,11 @@ public:
while(curr != next) {
const auto lhs = copy[curr];
const auto rhs = copy[next];
std::swap(instances[lhs], instances[rhs]);
if constexpr(!std::is_empty_v<object_type>) {
std::swap(instances[lhs], instances[rhs]);
}
underlying_type::swap(lhs, rhs);
copy[curr] = curr;
curr = next;

View File

@@ -9,6 +9,7 @@
#include <entt/entity/sparse_set.hpp>
struct empty_type {};
struct boxed_int { int value; };
TEST(SparseSetNoType, Functionalities) {
entt::sparse_set<std::uint64_t> set;
@@ -800,110 +801,110 @@ TEST(SparseSetWithType, RawEmptyType) {
}
TEST(SparseSetWithType, SortOrdered) {
entt::sparse_set<std::uint64_t, int> set;
entt::sparse_set<std::uint64_t, boxed_int> set;
set.construct(12, 12);
set.construct(42, 9);
set.construct(7, 6);
set.construct(3, 3);
set.construct(9, 1);
set.construct(12, boxed_int{12});
set.construct(42, boxed_int{9});
set.construct(7, boxed_int{6});
set.construct(3, boxed_int{3});
set.construct(9, boxed_int{1});
ASSERT_EQ(set.get(12), 12);
ASSERT_EQ(set.get(42), 9);
ASSERT_EQ(set.get(7), 6);
ASSERT_EQ(set.get(3), 3);
ASSERT_EQ(set.get(9), 1);
ASSERT_EQ(set.get(12).value, 12);
ASSERT_EQ(set.get(42).value, 9);
ASSERT_EQ(set.get(7).value, 6);
ASSERT_EQ(set.get(3).value, 3);
ASSERT_EQ(set.get(9).value, 1);
set.sort([](auto lhs, auto rhs) {
return lhs < rhs;
return lhs.value < rhs.value;
});
ASSERT_EQ(*(set.raw() + 0u), 12);
ASSERT_EQ(*(set.raw() + 1u), 9);
ASSERT_EQ(*(set.raw() + 2u), 6);
ASSERT_EQ(*(set.raw() + 3u), 3);
ASSERT_EQ(*(set.raw() + 4u), 1);
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), 1);
ASSERT_EQ(*(begin++), 3);
ASSERT_EQ(*(begin++), 6);
ASSERT_EQ(*(begin++), 9);
ASSERT_EQ(*(begin++), 12);
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
ASSERT_EQ((begin++)->value, 6);
ASSERT_EQ((begin++)->value, 9);
ASSERT_EQ((begin++)->value, 12);
ASSERT_EQ(begin, end);
}
TEST(SparseSetWithType, SortReverse) {
entt::sparse_set<std::uint64_t, int> set;
entt::sparse_set<std::uint64_t, boxed_int> set;
set.construct(12, 1);
set.construct(42, 3);
set.construct(7, 6);
set.construct(3, 9);
set.construct(9, 12);
set.construct(12, boxed_int{1});
set.construct(42, boxed_int{3});
set.construct(7, boxed_int{6});
set.construct(3, boxed_int{9});
set.construct(9, boxed_int{12});
ASSERT_EQ(set.get(12), 1);
ASSERT_EQ(set.get(42), 3);
ASSERT_EQ(set.get(7), 6);
ASSERT_EQ(set.get(3), 9);
ASSERT_EQ(set.get(9), 12);
ASSERT_EQ(set.get(12).value, 1);
ASSERT_EQ(set.get(42).value, 3);
ASSERT_EQ(set.get(7).value, 6);
ASSERT_EQ(set.get(3).value, 9);
ASSERT_EQ(set.get(9).value, 12);
set.sort([](auto lhs, auto rhs) {
return lhs < rhs;
set.sort([&set](std::uint64_t lhs, std::uint64_t rhs) {
return set.get(lhs).value < set.get(rhs).value;
});
ASSERT_EQ(*(set.raw() + 0u), 12);
ASSERT_EQ(*(set.raw() + 1u), 9);
ASSERT_EQ(*(set.raw() + 2u), 6);
ASSERT_EQ(*(set.raw() + 3u), 3);
ASSERT_EQ(*(set.raw() + 4u), 1);
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), 1);
ASSERT_EQ(*(begin++), 3);
ASSERT_EQ(*(begin++), 6);
ASSERT_EQ(*(begin++), 9);
ASSERT_EQ(*(begin++), 12);
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
ASSERT_EQ((begin++)->value, 6);
ASSERT_EQ((begin++)->value, 9);
ASSERT_EQ((begin++)->value, 12);
ASSERT_EQ(begin, end);
}
TEST(SparseSetWithType, SortUnordered) {
entt::sparse_set<std::uint64_t, int> set;
entt::sparse_set<std::uint64_t, boxed_int> set;
set.construct(12, 6);
set.construct(42, 3);
set.construct(7, 1);
set.construct(3, 9);
set.construct(9, 12);
set.construct(12, boxed_int{6});
set.construct(42, boxed_int{3});
set.construct(7, boxed_int{1});
set.construct(3, boxed_int{9});
set.construct(9, boxed_int{12});
ASSERT_EQ(set.get(12), 6);
ASSERT_EQ(set.get(42), 3);
ASSERT_EQ(set.get(7), 1);
ASSERT_EQ(set.get(3), 9);
ASSERT_EQ(set.get(9), 12);
ASSERT_EQ(set.get(12).value, 6);
ASSERT_EQ(set.get(42).value, 3);
ASSERT_EQ(set.get(7).value, 1);
ASSERT_EQ(set.get(3).value, 9);
ASSERT_EQ(set.get(9).value, 12);
set.sort([](auto lhs, auto rhs) {
return lhs < rhs;
return lhs.value < rhs.value;
});
ASSERT_EQ(*(set.raw() + 0u), 12);
ASSERT_EQ(*(set.raw() + 1u), 9);
ASSERT_EQ(*(set.raw() + 2u), 6);
ASSERT_EQ(*(set.raw() + 3u), 3);
ASSERT_EQ(*(set.raw() + 4u), 1);
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), 1);
ASSERT_EQ(*(begin++), 3);
ASSERT_EQ(*(begin++), 6);
ASSERT_EQ(*(begin++), 9);
ASSERT_EQ(*(begin++), 12);
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
ASSERT_EQ((begin++)->value, 6);
ASSERT_EQ((begin++)->value, 9);
ASSERT_EQ((begin++)->value, 12);
ASSERT_EQ(begin, end);
}