57 using ContainsFn = std::function<bool(
const glm::vec3&)>;
104 : m_fn_name(
std::move(fn_name))
121 std::string on_enter_fn_name,
122 std::string on_exit_fn_name,
126 : m_fn_name(
std::move(fn_name))
127 , m_on_enter_fn_name(
std::move(on_enter_fn_name))
128 , m_on_exit_fn_name(
std::move(on_exit_fn_name))
138 [[nodiscard]]
virtual bool contains(
const glm::vec3& p)
const
140 if (m_bounds && !m_bounds->contains(p)) {
143 return m_contains ? m_contains(p) : m_bounds.has_value();
153 [[nodiscard]]
const std::optional<Kinesis::AABB3D>&
bounds()
const {
return m_bounds; }
156 [[nodiscard]]
const std::string&
fn_name()
const {
return m_fn_name; }
168 [[nodiscard]]
const std::string&
on_exit_fn_name()
const {
return m_on_exit_fn_name; }
174 [[nodiscard]] uint32_t
id()
const {
return m_id; }
177 [[nodiscard]]
const std::unordered_set<uint32_t>*
occupants(uint32_t fabric_id)
const
179 auto it = m_occupants_by_fabric.find(fabric_id);
180 return it != m_occupants_by_fabric.end() ? &it->second :
nullptr;
186 std::vector<uint32_t> result;
187 result.reserve(m_occupants_by_fabric.size());
188 for (
const auto& [fid, _] : m_occupants_by_fabric)
189 result.push_back(fid);
199 template <
typename T>
200 std::shared_ptr<Influence<T, CrossingContext>>
add_entry(
203 return add_action<T>(m_entries, std::move(producer));
212 template <
typename T>
213 std::shared_ptr<Influence<T, CrossingContext>>
add_exit(
216 return add_action<T>(m_exits, std::move(producer));
225 template <
typename T>
229 return add_action<T>(m_insides, std::move(producer));
233 template <
typename T>
236 for (
auto* list : { &m_entries, &m_exits, &m_insides }) {
237 std::erase_if(*list, [&action](
const auto& entry) {
return entry.first == action; });
259 template <
typename T>
262 auto perception = std::make_shared<Perception<T>>(std::move(source));
263 m_perceptions.emplace_back(perception,
269 template <
typename T>
272 std::erase_if(m_perceptions,
273 [&perception](
const auto& entry) {
return entry.first == perception; });
291 void evaluate(uint32_t fabric_id,
292 std::span<
const std::pair<uint32_t, glm::vec3>> snapshot);
302 void evict(uint32_t fabric_id);
314 [[nodiscard]]
virtual std::pair<glm::vec3, float>
reach()
const
317 return { m_bounds->center(), glm::length(m_bounds->half_extent()) };
319 return { glm::vec3(0.0F), 0.0F };
327 template <
typename T>
328 static std::shared_ptr<Influence<T, CrossingContext>>
add_action(
332 auto action = std::make_shared<Influence<T, CrossingContext>>(std::move(producer));
333 list.emplace_back(action,
351 static uint32_t allocate_id();
353 uint32_t m_id { allocate_id() };
Expanse::ContainsFn contains
Expanse::CrossingFn on_enter
Expanse::CrossingFn on_exit
virtual bool contains(const glm::vec3 &p) const
Test whether a world position lies within the Expanse.
void set_on_exit_fn_name(std::string name)
Set or replace the on_exit callable identifier.
std::shared_ptr< Influence< T, CrossingContext > > add_inside(typename Influence< T, CrossingContext >::Producer producer)
Add an action of any type that runs on every commit for each entity inside.
std::vector< std::pair< std::shared_ptr< void >, Action > > Actions
const std::string & on_enter_fn_name() const
Identifier assigned to the on_enter callable, empty if anonymous.
const std::unordered_set< uint32_t > * occupants(uint32_t fabric_id) const
Entity ids inside this Expanse for fabric_id, or nullptr if that fabric has no occupants.
std::string m_on_enter_fn_name
std::unordered_map< uint32_t, std::unordered_set< uint32_t > > m_occupants_by_fabric
std::function< void(uint32_t)> CrossingFn
std::shared_ptr< Influence< T, CrossingContext > > add_entry(typename Influence< T, CrossingContext >::Producer producer)
Add an action of any type that runs when an entity enters.
virtual ~Expanse()=default
std::optional< Kinesis::AABB3D > m_bounds
std::vector< uint32_t > occupied_fabrics() const
All fabric ids that currently have at least one occupant inside this Expanse.
void set_fn_name(std::string name)
Set or replace the predicate identifier.
std::shared_ptr< Influence< T, CrossingContext > > add_exit(typename Influence< T, CrossingContext >::Producer producer)
Add an action of any type that runs when an entity leaves.
const std::string & on_exit_fn_name() const
Identifier assigned to the on_exit callable, empty if anonymous.
std::shared_ptr< Perception< T > > add_perception(typename Perception< T >::Source source)
Add a perception of any type over the entities inside, with as many targets as are wanted.
std::function< void(const CrossingContext &)> Action
std::string m_on_exit_fn_name
const std::optional< Kinesis::AABB3D > & bounds() const
The box, if set.
std::vector< std::pair< std::shared_ptr< void >, PerceptionFn > > m_perceptions
virtual void set_bounds(const Kinesis::AABB3D &bounds)
Set or replace the box.
void clear_crossings()
Stop every typed action.
std::function< bool(const glm::vec3 &)> ContainsFn
void clear_perceptions()
Stop every perception added with add_perception().
uint32_t id() const
Id assigned at construction.
const std::string & fn_name() const
Identifier assigned to the containment predicate, empty if anonymous.
Expanse(const Kinesis::AABB3D &bounds, CrossingFn on_enter={}, CrossingFn on_exit={})
Construct as a box.
void remove_crossing(const std::shared_ptr< Influence< T, CrossingContext > > &action)
Stop an action added with add_entry(), add_exit() or add_inside().
Expanse(std::string fn_name, std::string on_enter_fn_name, std::string on_exit_fn_name, ContainsFn contains, CrossingFn on_enter, CrossingFn on_exit)
Construct with named containment predicate and named crossing actions.
void set_on_enter_fn_name(std::string name)
Set or replace the on_enter callable identifier.
Expanse(ContainsFn contains)
Construct with a containment predicate only, for an Expanse whose behaviour comes from typed actions.
std::function< void(const PerceptionContext &)> PerceptionFn
virtual std::pair< glm::vec3, float > reach() const
Where the region is centred and how far it reaches, for perceptions.
static std::shared_ptr< Influence< T, CrossingContext > > add_action(Actions &list, typename Influence< T, CrossingContext >::Producer producer)
void clear_bounds()
Remove the box, leaving the predicate.
Expanse(ContainsFn contains, CrossingFn on_enter, CrossingFn on_exit)
Construct with a containment predicate and crossing actions.
virtual void begin_evaluate()
Called at the start of every evaluate(), before any position is tested.
Expanse(std::string fn_name, ContainsFn contains, CrossingFn on_enter, CrossingFn on_exit)
Construct with a named containment predicate and crossing actions.
void remove_perception(const std::shared_ptr< Perception< T > > &perception)
Stop a perception added with add_perception().
A defined region of space with a containment test and crossing actions.
std::function< T(const Context &)> Producer
A value of any type, built from the influence context and handed to its targets.
std::function< T(const PerceptionContext &)> Source
A value of any type, read from the world through the perception context and handed to its targets.
Axis-aligned bounding box in 3D world space.
std::optional< glm::vec3 > position
Position at this commit. Empty for an exit when the entity has left the Fabric.
uint32_t fabric_id
Id of the Fabric that evaluated the Expanse.
uint32_t entity
Id of the entity within that Fabric.
Data passed to a typed Expanse action for one entity.
Data passed to a Sensor or Agent perception function on each commit.