sort allows to work with entities
This commit is contained in:
2
TODO
2
TODO
@@ -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
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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)...);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user