wowlab_engine_ports/
spatial.rs1use std::fmt::Debug;
4
5use wowlab_types::sim::{EnemyIdx, Position2, SpatialLayerId, SpatialTransform};
6
7#[derive(Clone, Copy, Debug, PartialEq)]
9pub struct SpatialEnvelope {
10 pub min: Position2,
11 pub max: Position2,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq)]
16pub struct SpatialActor {
17 pub id: EnemyIdx,
18 pub transform: SpatialTransform,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq)]
23pub struct RadiusQuery {
24 pub layer: SpatialLayerId,
25 pub center: Position2,
26 pub radius: f64,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq)]
31pub struct ConeQuery {
32 pub layer: SpatialLayerId,
33 pub apex: Position2,
34 pub heading: f64,
35 pub half_angle: f64,
36 pub range: f64,
37}
38
39wowlab_engine_macros::define_error! {
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
42pub struct SpatialQueryError {
43 kind: SpatialQueryErrorKind,
44}
45
46#[derive(Clone, Copy, Debug, thiserror::Error, Eq, PartialEq)]
47enum SpatialQueryErrorKind {
48 #[error("actor {actor:?} is already present in the spatial index")]
49 DuplicateActor { actor: EnemyIdx },
50 #[error("actor {actor:?} is absent from the spatial index")]
51 MissingActor { actor: EnemyIdx },
52 #[error("actor {actor:?} spatial transform does not match the indexed transform")]
53 TransformMismatch { actor: EnemyIdx },
54 #[error("spatial transform is non-finite")]
55 NonFiniteTransform,
56 #[error("spatial transform is inside or on a static blocker")]
57 BlockedTransform,
58 #[error("spatial layer {layer:?} does not exist")]
59 MissingLayer { layer: SpatialLayerId },
60}
61}
62
63impl SpatialQueryError {
64 #[must_use]
66 pub const fn duplicate_actor(actor: EnemyIdx) -> Self {
67 Self {
68 kind: SpatialQueryErrorKind::DuplicateActor { actor },
69 }
70 }
71
72 #[must_use]
74 pub const fn missing_actor(actor: EnemyIdx) -> Self {
75 Self {
76 kind: SpatialQueryErrorKind::MissingActor { actor },
77 }
78 }
79
80 #[must_use]
82 pub const fn transform_mismatch(actor: EnemyIdx) -> Self {
83 Self {
84 kind: SpatialQueryErrorKind::TransformMismatch { actor },
85 }
86 }
87
88 #[must_use]
90 pub const fn non_finite_transform() -> Self {
91 Self {
92 kind: SpatialQueryErrorKind::NonFiniteTransform,
93 }
94 }
95
96 #[must_use]
98 pub const fn blocked_transform() -> Self {
99 Self {
100 kind: SpatialQueryErrorKind::BlockedTransform,
101 }
102 }
103
104 #[must_use]
106 pub const fn missing_layer(layer: SpatialLayerId) -> Self {
107 Self {
108 kind: SpatialQueryErrorKind::MissingLayer { layer },
109 }
110 }
111}
112
113pub trait SpatialQuery: Debug + Send {
115 fn candidates_on_layer(&self, layer: SpatialLayerId) -> Vec<EnemyIdx>;
117
118 fn candidates_in_envelope(
119 &self,
120 layer: SpatialLayerId,
121 envelope: SpatialEnvelope,
122 ) -> Vec<EnemyIdx>;
123
124 fn actors_in_radius(&self, query: RadiusQuery, allowed: Option<&[EnemyIdx]>) -> Vec<EnemyIdx>;
125
126 fn actors_in_cone(&self, query: ConeQuery, allowed: Option<&[EnemyIdx]>) -> Vec<EnemyIdx>;
127
128 fn actors_by_distance(
130 &self,
131 layer: SpatialLayerId,
132 origin: Position2,
133 allowed: Option<&[EnemyIdx]>,
134 ) -> Vec<EnemyIdx>;
135
136 fn nearest_actor_matching(
138 &self,
139 layer: SpatialLayerId,
140 origin: Position2,
141 is_eligible: &mut dyn FnMut(EnemyIdx) -> bool,
142 ) -> Option<EnemyIdx>;
143
144 fn segment_visible(&self, layer: SpatialLayerId, start: Position2, end: Position2) -> bool;
146
147 fn validate_transform(&self, transform: SpatialTransform) -> Result<(), SpatialQueryError>;
151
152 fn insert_actor(&mut self, actor: SpatialActor) -> Result<(), SpatialQueryError>;
156
157 fn remove_actor(&mut self, actor: SpatialActor) -> Result<(), SpatialQueryError>;
161
162 fn relocate_actor(
166 &mut self,
167 actor: EnemyIdx,
168 from: SpatialTransform,
169 to: SpatialTransform,
170 ) -> Result<(), SpatialQueryError>;
171}