Skip to content

Commit

Permalink
view supports exclusion list
Browse files Browse the repository at this point in the history
  • Loading branch information
skypjack committed Sep 18, 2019
1 parent 6649362 commit 90eeeed
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 52 deletions.
1 change: 0 additions & 1 deletion TODO
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,3 @@ TODO
* range based registry::remove and some others?
* nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
- sink::before and ordered calls
* view and exclusion list
23 changes: 8 additions & 15 deletions docs/md/entity.md
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,12 @@ auto single = registry.view<position>();
auto multi = registry.view<position, velocity>();
```

Filtering entities by components is also supported:

```cpp
auto view = registry.view<position, velocity>(entt::exclude<renderable>);
```

To iterate a view, either use it in a range-for loop:

```cpp
Expand Down Expand Up @@ -1141,21 +1147,8 @@ There exists also an alternative version of `each` named `less` that works
exactly as its counterpart but for the fact that it doesn't return empty
components to the caller.

As a side note, when using a single component view, the most common error is to
invoke `get` with the type of the component as a template parameter. This is
probably due to the fact that it's required for multi component views:

```cpp
auto view = registry.view<position, const velocity>();

for(auto entity: view) {
const auto &vel = view.get<const velocity>(entity);
// ...
}
```

However, in case of a single component view, `get` doesn't accept a template
parameter, since it's implicitly defined by the view itself:
As a side note, in the case of single component views, `get` accepts but doesn't
strictly require a template parameter, since the type is implicitly defined:

```cpp
auto view = registry.view<const renderable>();
Expand Down
58 changes: 33 additions & 25 deletions src/entt/entity/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ class basic_view;
*/
template<typename Entity, typename... Exclude, typename... Component>
class basic_view<Entity, exclude_t<Exclude...>, Component...> {
static_assert(sizeof...(Component) > 1);

/*! @brief A registry is allowed to create views. */
friend class basic_registry<Entity>;

Expand All @@ -82,25 +80,24 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {

using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
using traits_type = entt_traits<std::underlying_type_t<Entity>>;

class iterator {
friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;

iterator(unchecked_type other, underlying_iterator_type first, underlying_iterator_type last) ENTT_NOEXCEPT
: unchecked{other},
begin{first},
end{last}
iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, std::tuple<pool_type<Exclude> *...> exclude) ENTT_NOEXCEPT
: begin{first},
end{last},
unchecked{other},
filter{exclude}
{
if(begin != end && !valid()) {
++(*this);
}
}

bool valid() const ENTT_NOEXCEPT {
return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) {
return view->has(*begin);
});
return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); })
&& (!std::get<pool_type<Exclude> *>(filter)->has(*begin) && ...);
}

public:
Expand Down Expand Up @@ -138,14 +135,16 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
}

private:
unchecked_type unchecked;
underlying_iterator_type begin;
underlying_iterator_type end;
unchecked_type unchecked;
std::tuple<pool_type<Exclude> *...> filter;
};

// we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
basic_view(storage<Entity, std::remove_const_t<Component>> *... ref) ENTT_NOEXCEPT
: pools{ref...}
basic_view(storage<Entity, std::remove_const_t<Component>> *... component, storage<Entity, std::remove_const_t<Exclude>> *... exclude) ENTT_NOEXCEPT
: pools{component...},
filter{exclude...}
{}

const sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
Expand Down Expand Up @@ -179,7 +178,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
std::for_each(begin, end, [this, raw = std::get<pool_type<Comp> *>(pools)->begin(), &func](const auto entity) mutable {
auto curr = raw++;

if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
func(get<Comp, Type>(curr, std::get<pool_type<Type> *>(pools), entity)...);
} else {
Expand All @@ -188,8 +187,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
}
});
} else {
std::for_each(begin, end, [this, &func](const auto entity) mutable {
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
std::for_each(begin, end, [this, &func](const auto entity) {
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...) && (!std::get<pool_type<Exclude> *>(filter)->has(entity) && ...)) {
if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
func(std::get<pool_type<Type> *>(pools)->get(entity)...);
} else {
Expand All @@ -210,6 +209,9 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {

/**
* @brief Returns the number of existing components of the given type.
*
* This isn't the number of entities iterated by the view.
*
* @tparam Comp Type of component of which to return the size.
* @return Number of existing components of the given type.
*/
Expand All @@ -219,8 +221,8 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
}

/**
* @brief Estimates the number of entities that have the given components.
* @return Estimated number of entities that have the given components.
* @brief Estimates the number of entities iterated by the view.
* @return Estimated number of entities iterated by the view.
*/
size_type size() const ENTT_NOEXCEPT {
return std::min({ std::get<pool_type<Component> *>(pools)->size()... });
Expand All @@ -230,9 +232,9 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
* @brief Checks whether the view or the pools of the given components are
* empty.
*
* The view is definitely empty if one of the pools is empty. In all other
* cases, the view may be empty and not return entities even if this
* function returns false.
* The view is definitely empty if one of the pools of the given components
* is empty. In all other cases, the view may be empty and not return
* entities even if this function returns false.
*
* @tparam Comp Types of components in which one is interested.
* @return True if the view or the pools of the given components are empty,
Expand Down Expand Up @@ -301,7 +303,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
*/
iterator_type begin() const ENTT_NOEXCEPT {
const auto *view = candidate();
return iterator_type{unchecked(view), view->begin(), view->end()};
return iterator_type{view->begin(), view->end(), unchecked(view), filter};
}

/**
Expand All @@ -321,7 +323,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
*/
iterator_type end() const ENTT_NOEXCEPT {
const auto *view = candidate();
return iterator_type{unchecked(view), view->end(), view->end()};
return iterator_type{view->end(), view->end(), unchecked(view), filter};
}

/**
Expand All @@ -332,7 +334,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
*/
iterator_type find(const entity_type entt) const ENTT_NOEXCEPT {
const auto *view = candidate();
iterator_type it{unchecked(view), view->find(entt), view->end()};
iterator_type it{view->find(entt), view->end(), unchecked(view), filter};
return (it != end() && *it == entt) ? it : end();
}

Expand Down Expand Up @@ -366,7 +368,10 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
ENTT_ASSERT(contains(entt));

if constexpr(sizeof...(Comp) == 1) {
if constexpr(sizeof...(Comp) == 0) {
static_assert(sizeof...(Component) == 1);
return (std::get<pool_type<Component> *>(pools)->get(entt), ...);
} else if constexpr(sizeof...(Comp) == 1) {
return (std::get<pool_type<Comp> *>(pools)->get(entt), ...);
} else {
return std::tuple<decltype(get<Comp>({}))...>{get<Comp>(entt)...};
Expand Down Expand Up @@ -500,6 +505,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {

private:
const std::tuple<pool_type<Component> *...> pools;
const std::tuple<pool_type<Exclude> *...> filter;
};


Expand Down Expand Up @@ -686,7 +692,9 @@ class basic_view<Entity, exclude_t<>, Component> {
* @param entt A valid entity identifier.
* @return The component assigned to the entity.
*/
template<typename Comp = Component>
decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT {
static_assert(std::is_same_v<Comp, Component>);
ENTT_ASSERT(contains(entt));
return pool->get(entt);
}
Expand Down
67 changes: 56 additions & 11 deletions test/entt/entity/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ TEST(SingleComponentView, Functionalities) {

ASSERT_EQ(view.size(), typename decltype(view)::size_type{2});

view.get(e0) = '1';
view.get<char>(e0) = '1';
view.get(e1) = '2';

for(auto entity: view) {
ASSERT_TRUE(cview.get(entity) == '1' || cview.get(entity) == '2');
ASSERT_TRUE(cview.get<const char>(entity) == '1' || cview.get(entity) == '2');
}

ASSERT_EQ(*(view.data() + 0), e1);
Expand Down Expand Up @@ -214,7 +214,7 @@ TEST(SingleComponentView, Less) {
});
}

TEST(MultipleComponentView, Functionalities) {
TEST(MultiComponentView, Functionalities) {
entt::registry registry;
auto view = registry.view<int, char>();
auto cview = std::as_const(registry).view<const int, const char>();
Expand Down Expand Up @@ -268,7 +268,7 @@ TEST(MultipleComponentView, Functionalities) {
ASSERT_EQ(*(cview.raw<const char>() + 1), '2');
}

TEST(MultipleComponentView, Iterator) {
TEST(MultiComponentView, Iterator) {
entt::registry registry;
const auto entity = registry.create();
registry.assign<int>(entity);
Expand All @@ -290,7 +290,7 @@ TEST(MultipleComponentView, Iterator) {
ASSERT_EQ(++view.begin(), view.end());
}

TEST(MultipleComponentView, Contains) {
TEST(MultiComponentView, Contains) {
entt::registry registry;

const auto e0 = registry.create();
Expand All @@ -309,7 +309,7 @@ TEST(MultipleComponentView, Contains) {
ASSERT_TRUE(view.contains(e1));
}

TEST(MultipleComponentView, Empty) {
TEST(MultiComponentView, Empty) {
entt::registry registry;

const auto e0 = registry.create();
Expand All @@ -327,7 +327,7 @@ TEST(MultipleComponentView, Empty) {
ASSERT_EQ(view.begin(), view.end());
}

TEST(MultipleComponentView, Each) {
TEST(MultiComponentView, Each) {
entt::registry registry;

const auto e0 = registry.create();
Expand All @@ -353,7 +353,7 @@ TEST(MultipleComponentView, Each) {
ASSERT_EQ(cnt, std::size_t{0});
}

TEST(MultipleComponentView, EachWithType) {
TEST(MultiComponentView, EachWithType) {
entt::registry registry;

for(auto i = 0; i < 3; ++i) {
Expand All @@ -379,7 +379,7 @@ TEST(MultipleComponentView, EachWithType) {
});
}

TEST(MultipleComponentView, EachWithHoles) {
TEST(MultiComponentView, EachWithHoles) {
entt::registry registry;

const auto e0 = registry.create();
Expand All @@ -404,7 +404,7 @@ TEST(MultipleComponentView, EachWithHoles) {
});
}

TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
TEST(MultiComponentView, ConstNonConstAndAllInBetween) {
entt::registry registry;
auto view = registry.view<int, const char, std::true_type>();

Expand All @@ -431,7 +431,7 @@ TEST(MultipleComponentView, ConstNonConstAndAllInBetween) {
});
}

TEST(MultipleComponentView, Find) {
TEST(MultiComponentView, Find) {
entt::registry registry;
auto view = registry.view<int, const char>();

Expand Down Expand Up @@ -476,6 +476,51 @@ TEST(MultipleComponentView, Find) {
ASSERT_EQ(view.find(e4), view.end());
}

TEST(MultiComponentView, ExcludedComponents) {
entt::registry registry;

const auto e0 = registry.create();
registry.assign<int>(e0, 0);

const auto e1 = registry.create();
registry.assign<int>(e1, 1);
registry.assign<char>(e1);

const auto view = registry.view<int>(entt::exclude<char>);

const auto e2 = registry.create();
registry.assign<int>(e2, 2);

const auto e3 = registry.create();
registry.assign<int>(e3, 3);
registry.assign<char>(e3);

for(const auto entity: view) {
if(entity == e0) {
ASSERT_EQ(view.get<int>(e0), 0);
} else if(entity == e2) {
ASSERT_EQ(view.get(e2), 2);
} else {
FAIL();
}
}

registry.assign<char>(e0);
registry.assign<char>(e2);
registry.remove<char>(e1);
registry.remove<char>(e3);

for(const auto entity: view) {
if(entity == e1) {
ASSERT_EQ(view.get(e1), 1);
} else if(entity == e3) {
ASSERT_EQ(view.get<int>(e3), 3);
} else {
FAIL();
}
}
}

TEST(MultiComponentView, Less) {
entt::registry registry;
const auto entity = std::get<0>(registry.create<int, char, double, entt::tag<"empty"_hs>>());
Expand Down

0 comments on commit 90eeeed

Please sign in to comment.