Просмотр исходного кода

sort allows to work with entities

Michele Caini 7 лет назад
Родитель
Сommit
96f267cbfd
5 измененных файлов с 122 добавлено и 105 удалено
  1. 1 1
      TODO
  2. 25 20
      docs/md/entity.md
  3. 3 3
      src/entt/entity/registry.hpp
  4. 22 11
      src/entt/entity/sparse_set.hpp
  5. 71 70
      test/entt/entity/sparse_set.cpp

+ 1 - 1
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

+ 25 - 20
docs/md/entity.md

@@ -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
 });

+ 3 - 3
src/entt/entity/registry.hpp

@@ -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)...);
     }
 
     /**

+ 22 - 11
src/entt/entity/sparse_set.hpp

@@ -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;

+ 71 - 70
test/entt/entity/sparse_set.cpp

@@ -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;
-
-    set.construct(12, 1);
-    set.construct(42, 3);
-    set.construct(7, 6);
-    set.construct(3, 9);
-    set.construct(9, 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);
-
-    set.sort([](auto lhs, auto rhs) {
-        return lhs < rhs;
+    entt::sparse_set<std::uint64_t, boxed_int> set;
+
+    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).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([&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);
 }