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 {
117 fn actor_capacity(&self) -> usize;
119
120 fn candidates_on_layer_into(&self, layer: SpatialLayerId, output: &mut [EnemyIdx]) -> usize;
122
123 fn candidates_in_envelope_into(
124 &self,
125 layer: SpatialLayerId,
126 envelope: SpatialEnvelope,
127 output: &mut [EnemyIdx],
128 ) -> usize;
129
130 fn actors_in_radius_into(
131 &self,
132 query: RadiusQuery,
133 allowed: Option<&[EnemyIdx]>,
134 output: &mut [EnemyIdx],
135 ) -> usize;
136
137 fn actors_in_cone_into(
138 &self,
139 query: ConeQuery,
140 allowed: Option<&[EnemyIdx]>,
141 output: &mut [EnemyIdx],
142 ) -> usize;
143
144 fn actors_by_distance_into(
146 &self,
147 layer: SpatialLayerId,
148 origin: Position2,
149 allowed: Option<&[EnemyIdx]>,
150 output: &mut [EnemyIdx],
151 ) -> usize;
152
153 fn segment_visible(&self, layer: SpatialLayerId, start: Position2, end: Position2) -> bool;
155
156 fn validate_transform(&self, transform: SpatialTransform) -> Result<(), SpatialQueryError>;
160
161 fn insert_actor(&mut self, actor: SpatialActor) -> Result<(), SpatialQueryError>;
165
166 fn remove_actor(&mut self, actor: SpatialActor) -> Result<(), SpatialQueryError>;
170
171 fn relocate_actor(
175 &mut self,
176 actor: EnemyIdx,
177 from: SpatialTransform,
178 to: SpatialTransform,
179 ) -> Result<(), SpatialQueryError>;
180}