repose_collision/
scene_exclusions.rs1use crate::env::CuCollisionEnv;
16use crate::scene::CuObjectId;
17use crate::scene::CuPlanningScene;
18use crate::scene::CuSceneError;
19use bincode::Decode;
20use bincode::Encode;
21use cu29::prelude::*;
22use serde::Deserialize;
23use serde::Serialize;
24
25pub const DEFAULT_MAX_EXCLUSIONS: usize = 16;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Serialize, Deserialize, Reflect)]
30#[serde(rename_all = "snake_case")]
31pub enum CuSceneExclusionPartner {
32 RobotShape {
34 shape_index: u16,
36 },
37 Attached {
39 attached_id: CuObjectId,
41 },
42 Obstacle {
44 obstacle_id: CuObjectId,
46 },
47}
48
49#[derive(
54 Debug, Clone, Copy, Default, PartialEq, Eq, Encode, Decode, Serialize, Deserialize, Reflect,
55)]
56pub struct CuSceneExclusion {
57 pub attached_id: CuObjectId,
59 pub partner: CuSceneExclusionPartner,
61}
62
63impl Default for CuSceneExclusionPartner {
64 fn default() -> Self {
65 Self::RobotShape { shape_index: 0 }
66 }
67}
68
69impl CuSceneExclusion {
70 pub const NIL: Self = Self {
72 attached_id: CuObjectId::NIL,
73 partner: CuSceneExclusionPartner::RobotShape { shape_index: 0 },
74 };
75
76 pub const fn robot(attached_id: CuObjectId, shape_index: u16) -> Self {
78 Self {
79 attached_id,
80 partner: CuSceneExclusionPartner::RobotShape { shape_index },
81 }
82 }
83
84 pub const fn attached(attached_id: CuObjectId, other_id: CuObjectId) -> Self {
86 Self {
87 attached_id,
88 partner: CuSceneExclusionPartner::Attached {
89 attached_id: other_id,
90 },
91 }
92 }
93
94 pub const fn obstacle(attached_id: CuObjectId, obstacle_id: CuObjectId) -> Self {
96 Self {
97 attached_id,
98 partner: CuSceneExclusionPartner::Obstacle { obstacle_id },
99 }
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Reflect)]
108#[reflect(no_field_bounds, from_reflect = false)]
109pub struct CuSceneExclusions<const MAX_EXCLUSIONS: usize = DEFAULT_MAX_EXCLUSIONS> {
110 #[serde(with = "crate::serde_arrays::exclusion_array")]
111 exclusions: [CuSceneExclusion; MAX_EXCLUSIONS],
112 len: u32,
113}
114
115impl<const MAX_EXCLUSIONS: usize> Default for CuSceneExclusions<MAX_EXCLUSIONS> {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl<const MAX_EXCLUSIONS: usize> Encode for CuSceneExclusions<MAX_EXCLUSIONS> {
122 fn encode<E: bincode::enc::Encoder>(
123 &self,
124 encoder: &mut E,
125 ) -> Result<(), bincode::error::EncodeError> {
126 Encode::encode(&self.len, encoder)?;
127 Encode::encode(&self.exclusions, encoder)
128 }
129}
130
131impl<C, const MAX_EXCLUSIONS: usize> Decode<C> for CuSceneExclusions<MAX_EXCLUSIONS> {
132 fn decode<D: bincode::de::Decoder<Context = C>>(
133 decoder: &mut D,
134 ) -> Result<Self, bincode::error::DecodeError> {
135 let len: u32 = Decode::decode(decoder)?;
136 let exclusions = Decode::decode(decoder)?;
137 if len as usize > MAX_EXCLUSIONS {
138 return Err(bincode::error::DecodeError::Other(
139 "Invalid scene exclusions snapshot: count exceeds capacity",
140 ));
141 }
142 Ok(Self { exclusions, len })
143 }
144}
145
146impl<'de, C, const MAX_EXCLUSIONS: usize> bincode::BorrowDecode<'de, C>
147 for CuSceneExclusions<MAX_EXCLUSIONS>
148{
149 fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = C>>(
150 decoder: &mut D,
151 ) -> Result<Self, bincode::error::DecodeError> {
152 Decode::decode(decoder)
153 }
154}
155
156impl<const MAX_EXCLUSIONS: usize> CuSceneExclusions<MAX_EXCLUSIONS> {
157 pub const fn new() -> Self {
159 Self {
160 exclusions: [CuSceneExclusion::NIL; MAX_EXCLUSIONS],
161 len: 0,
162 }
163 }
164
165 pub const fn len(&self) -> usize {
167 self.len as usize
168 }
169
170 pub const fn is_empty(&self) -> bool {
172 self.len == 0
173 }
174
175 pub const fn capacity(&self) -> usize {
177 MAX_EXCLUSIONS
178 }
179
180 pub fn as_slice(&self) -> &[CuSceneExclusion] {
182 &self.exclusions[..self.len as usize]
183 }
184
185 pub fn add(&mut self, exclusion: CuSceneExclusion) -> CuResult<()> {
187 let idx = self.len as usize;
188 if idx >= MAX_EXCLUSIONS {
189 return Err(CuError::from(
190 "repose_collision: scene exclusion capacity exceeded",
191 ));
192 }
193 self.exclusions[idx] = exclusion;
194 self.len += 1;
195 Ok(())
196 }
197
198 pub fn clear(&mut self) {
200 self.len = 0;
201 }
202
203 pub fn validate<const MAX_OBS: usize, const MAX_ATT: usize>(
208 &self,
209 scene: &CuPlanningScene<MAX_OBS, MAX_ATT>,
210 robot_shape_count: usize,
211 ) -> CuResult<()> {
212 validate_exclusions(self.as_slice(), &scene.collision_env(), robot_shape_count)
213 }
214}
215
216pub(crate) fn validate_exclusions(
218 exclusions: &[CuSceneExclusion],
219 env: &CuCollisionEnv<'_>,
220 robot_shape_count: usize,
221) -> CuResult<()> {
222 for entry in exclusions {
223 if !entry.attached_id.is_valid() {
224 return Err(CuSceneError::InvalidObjectId(entry.attached_id).into());
225 }
226 if !env.find_attached(entry.attached_id) {
227 return Err(CuSceneError::ObjectNotFound(entry.attached_id).into());
228 }
229 match entry.partner {
230 CuSceneExclusionPartner::RobotShape { shape_index } => {
231 if shape_index as usize >= robot_shape_count {
232 return Err(CuError::from(
233 "repose_collision: scene exclusion robot shape index out of range",
234 ));
235 }
236 }
237 CuSceneExclusionPartner::Attached { attached_id } => {
238 if !attached_id.is_valid() {
239 return Err(CuSceneError::InvalidObjectId(attached_id).into());
240 }
241 if attached_id == entry.attached_id {
242 return Err(CuError::from(
243 "repose_collision: scene exclusion references its own attached shape",
244 ));
245 }
246 if !env.find_attached(attached_id) {
247 return Err(CuSceneError::ObjectNotFound(attached_id).into());
248 }
249 }
250 CuSceneExclusionPartner::Obstacle { obstacle_id } => {
251 if !obstacle_id.is_valid() {
252 return Err(CuSceneError::InvalidObjectId(obstacle_id).into());
253 }
254 if !env.find_obstacle(obstacle_id) {
255 return Err(CuSceneError::ObjectNotFound(obstacle_id).into());
256 }
257 }
258 }
259 }
260 Ok(())
261}