-
Notifications
You must be signed in to change notification settings - Fork 304
/
Copy pathView.hh
336 lines (296 loc) · 12.7 KB
/
View.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
* Copyright (C) 2018 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef IGNITION_GAZEBO_DETAIL_VIEW_HH_
#define IGNITION_GAZEBO_DETAIL_VIEW_HH_
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <ignition/common/Console.hh>
#include "ignition/gazebo/Entity.hh"
#include "ignition/gazebo/config.hh"
#include "ignition/gazebo/detail/BaseView.hh"
namespace ignition
{
namespace gazebo
{
// Inline bracket to help doxygen filtering.
inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
namespace detail
{
/// \brief A view that caches a particular set of component type data.
/// \tparam ComponentTypeTs The component type(s) that are stored in this view.
template<typename ...ComponentTypeTs>
class View : public BaseView
{
/// \brief Alias for containers that hold and entity and its component data.
/// The component types held in this container match the component types that
/// were specified when creating the view.
private: using ComponentData = std::tuple<Entity, ComponentTypeTs*...>;
private: using ConstComponentData =
std::tuple<Entity, const ComponentTypeTs*...>;
/// \brief Constructor
public: View();
/// \brief Documentation inherited
public: bool HasCachedComponentData(const Entity _entity) const override;
/// \brief Documentation inherited
public: bool RemoveEntity(const Entity _entity) override;
/// \brief Get an entity and its component data. It is assumed that the entity
/// being requested exists in the view.
/// \param[_in] _entity The entity
/// \return The entity and its component data. Const pointers to the component
/// data are returned.
public: ConstComponentData EntityComponentConstData(
const Entity _entity) const;
/// \brief Get an entity and its component data. It is assumed that the entity
/// being requested exists in the view.
/// \param[_in] _entity The entity
/// \return The entity and its component data. Mutable pointers to the
/// component data are returned.
public: ComponentData EntityComponentData(const Entity _entity);
/// \brief Add an entity with its component data to the view. It is assunmed
/// that the entity to be added does not already exist in the view.
/// \param[in] _entity The entity
/// \param[in] _new Whether to add the entity to the list of new entities.
/// The new here is to indicate whether the entity is new to the entity
/// component manager. An existing entity can be added when creating a new
/// view or when rebuilding the view.
/// \param[in] _compPtrs Const pointers to the entity's components
public: void AddEntityWithConstComps(const Entity &_entity, const bool _new,
const ComponentTypeTs*... _compPtrs);
/// \brief Add an entity with its component data to the view. It is assunmed
/// that the entity to be added does not already exist in the view.
/// \param[in] _entity The entity
/// \param[in] _new Whether to add the entity to the list of new entities.
/// The new here is to indicate whether the entity is new to the entity
/// component manager. An existing entity can be added when creating a new
/// view or when rebuilding the view.
/// \param[in] _compPtrs Pointers to the entity's components
public: void AddEntityWithComps(const Entity &_entity, const bool _new,
ComponentTypeTs*... _compPtrs);
/// \brief Documentation inherited
public: bool NotifyComponentAddition(const Entity _entity, bool _newEntity,
const ComponentTypeId _typeId) override;
/// \brief Documentation inherited
public: bool NotifyComponentRemoval(const Entity _entity,
const ComponentTypeId _typeId) override;
/// \brief Documentation inherited
public: void Reset() override;
/// \brief A map of entities to their component data. Since tuples are defined
/// at compile time, we need seperate containers that have tuples for both
/// non-const and const component pointers (calls to ECM::Each can have a
/// method signature that uses either non-const or const pointers)
private: std::unordered_map<Entity, ComponentData> validData;
private: std::unordered_map<Entity, ConstComponentData> validConstData;
/// \brief A map of invalid entities to their component data. The difference
/// between invalidData and validData is that the entities in invalidData were
/// once in validData, but they had a component removed, so the entity no
/// longer meets the component requirements of the view. If the missing
/// component data is ever added back to an entitiy in invalidData, then this
/// entity will be moved back to validData. The usage of invalidData is an
/// implementation detail that should be ignored by those using the View API;
/// from a user's point of view, entities that belong to invalidData don't
/// appear to be a part of the view at all.
///
/// The reason for moving entities with missing components to invalidData
/// instead of completely deleting them from the view is because if components
/// are added back later and the entity needs to be re-added to the view,
/// tuple creation can be costly. So, this approach is used instead to
/// maintain runtime performance (the tradeoff of mainting performance is
/// increased complexity and memory usage).
///
/// \sa missingCompTracker
private: std::unordered_map<Entity, ComponentData> invalidData;
private: std::unordered_map<Entity, ConstComponentData> invalidConstData;
/// \brief A map that keeps track of which component types for entities in
/// invalidData need to be added back to the entity in order to move the
/// entity back to validData. If the set of types (value in the map) becomes
/// empty, then this means that the entity (key in the map) has all of the
/// component types defined by the view, so the entity can be moved back to
/// validData.
///
/// \sa invalidData
private: std::unordered_map<Entity, std::unordered_set<ComponentTypeId>>
missingCompTracker;
};
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
View<ComponentTypeTs...>::View()
{
this->componentTypes = {ComponentTypeTs::typeId...};
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
bool View<ComponentTypeTs...>::HasCachedComponentData(
const Entity _entity) const
{
auto cachedComps =
this->validData.find(_entity) != this->validData.end() ||
this->invalidData.find(_entity) != this->invalidData.end();
auto cachedConstComps =
this->validConstData.find(_entity) != this->validConstData.end() ||
this->invalidConstData.find(_entity) != this->invalidConstData.end();
if (cachedComps && !cachedConstComps)
{
ignwarn << "Non-const component data is cached for entity " << _entity
<< ", but const component data is not cached." << std::endl;
}
else if (cachedConstComps && !cachedComps)
{
ignwarn << "Const component data is cached for entity " << _entity
<< ", but non-const component data is not cached." << std::endl;
}
return cachedComps && cachedConstComps;
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
bool View<ComponentTypeTs...>::RemoveEntity(const Entity _entity)
{
this->invalidData.erase(_entity);
this->invalidConstData.erase(_entity);
this->missingCompTracker.erase(_entity);
if (!this->HasEntity(_entity) && !this->IsEntityMarkedForAddition(_entity))
return false;
this->entities.erase(_entity);
this->newEntities.erase(_entity);
this->toRemoveEntities.erase(_entity);
this->toAddEntities.erase(_entity);
this->validData.erase(_entity);
this->validConstData.erase(_entity);
return true;
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
typename View<ComponentTypeTs...>::ConstComponentData
View<ComponentTypeTs...>::EntityComponentConstData(const Entity _entity) const
{
return this->validConstData.at(_entity);
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
typename View<ComponentTypeTs...>::ComponentData
View<ComponentTypeTs...>::EntityComponentData(const Entity _entity)
{
return this->validData.at(_entity);
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
void View<ComponentTypeTs...>::AddEntityWithConstComps(const Entity &_entity,
const bool _new, const ComponentTypeTs*... _compPtrs)
{
this->validConstData[_entity] = std::make_tuple(_entity, _compPtrs...);
this->entities.insert(_entity);
if (_new)
this->newEntities.insert(_entity);
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
void View<ComponentTypeTs...>::AddEntityWithComps(const Entity &_entity,
const bool _new, ComponentTypeTs*... _compPtrs)
{
this->validData[_entity] = std::make_tuple(_entity, _compPtrs...);
this->entities.insert(_entity);
if (_new)
this->newEntities.insert(_entity);
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
bool View<ComponentTypeTs...>::NotifyComponentAddition(const Entity _entity,
bool _newEntity, const ComponentTypeId _typeId)
{
// make sure that _typeId is a type required by the view and that _entity is
// already a part of the view
if (!this->RequiresComponent(_typeId) ||
!this->HasCachedComponentData(_entity))
return false;
// remove the newly added component type from the missing component types
// list
auto missingCompsIter = this->missingCompTracker.find(_entity);
if (missingCompsIter == this->missingCompTracker.end())
{
// the component is already added, so nothing else needs to be done
return true;
}
missingCompsIter->second.erase(_typeId);
// if the entity now has all components that meet the requirements of the
// view, then add the entity back to the view
if (missingCompsIter->second.empty())
{
auto nh = this->invalidData.extract(_entity);
this->validData.insert(std::move(nh));
auto constCompNh = this->invalidConstData.extract(_entity);
this->validConstData.insert(std::move(constCompNh));
this->entities.insert(_entity);
if (_newEntity)
this->newEntities.insert(_entity);
this->missingCompTracker.erase(_entity);
}
return true;
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
bool View<ComponentTypeTs...>::NotifyComponentRemoval(const Entity _entity,
const ComponentTypeId _typeId)
{
// if entity is still marked as to add, remove from the view
if (this->RequiresComponent(_typeId))
this->toAddEntities.erase(_entity);
// make sure that _typeId is a type required by the view and that _entity is
// already a part of the view
if (!this->RequiresComponent(_typeId) ||
!this->HasCachedComponentData(_entity))
return false;
// if the component being removed is the first component that causes _entity
// to be invalid for this view, move _entity from validData to invalidData
// since _entity should no longer be considered a part of the view
auto it = this->validData.find(_entity);
auto constCompIt = this->validConstData.find(_entity);
if (it != this->validData.end() &&
constCompIt != this->validConstData.end())
{
auto nh = this->validData.extract(it);
this->invalidData.insert(std::move(nh));
auto constCompNh = this->validConstData.extract(constCompIt);
this->invalidConstData.insert(std::move(constCompNh));
this->entities.erase(_entity);
this->newEntities.erase(_entity);
}
this->missingCompTracker[_entity].insert(_typeId);
return true;
}
//////////////////////////////////////////////////
template<typename ...ComponentTypeTs>
void View<ComponentTypeTs...>::Reset()
{
// reset all data structures in the BaseView except for componentTypes since
// the view always requires the types in componentTypes
this->entities.clear();
this->newEntities.clear();
this->toRemoveEntities.clear();
this->toAddEntities.clear();
// reset all data structures unique to the templated view
this->validData.clear();
this->validConstData.clear();
this->invalidData.clear();
this->invalidConstData.clear();
this->missingCompTracker.clear();
}
} // namespace detail
} // namespace IGNITION_GAZEBO_VERSION_NAMESPACE
} // namespace gazebo
} // namespace ignition
#endif