Skip to main content

repose_collision/
env.rs

1//! Borrowed, zero-copy view of everything a robot is checked against.
2
3use crate::scene::CuAttachedShape;
4use crate::scene::CuObjectId;
5use crate::scene_exclusions::CuSceneExclusion;
6use crate::scene_exclusions::CuSceneExclusionPartner;
7use crate::scene_exclusions::CuSceneExclusions;
8use crate::shapes::CuCollisionShape;
9
10/// Borrowed view of the collision environment: world obstacles, attached shapes, and exclusions.
11///
12/// The view copies nothing. Build it from raw obstacle shapes with [`new`](Self::new), or from a
13/// planning scene with [`CuPlanningScene::collision_env`](crate::CuPlanningScene::collision_env)
14/// (which also carries obstacle IDs, so obstacle exclusions resolve). [`EMPTY`](Self::EMPTY)
15/// checks the robot against itself only.
16///
17/// # Attached shapes
18///
19/// Attached shapes are transformed by their frame (`None` = base frame) and checked against each
20/// other, the robot's own shapes, and the obstacles:
21///
22/// - attached vs. robot and attached vs. attached report [`CuContactKind::SelfCollision`];
23/// - attached vs. obstacle reports [`CuContactKind::EnvironmentCollision`].
24///
25/// Attached shapes on the same frame (including two `None`) are one rigid body and are never
26/// checked against each other. Attached-vs-robot pairs are never skipped automatically; exclude
27/// mounting contact explicitly with [`CuSceneExclusions`].
28///
29/// # Contact index encoding
30///
31/// For attached-shape contacts `link_index` is `robot_shape_count + attached_index`, so it never
32/// collides with robot shape indices `0..robot_shape_count`. `partner_index` is the robot shape
33/// index, `robot_shape_count + attached_index` for attached partners, or the obstacle index.
34///
35/// # Exclusions
36///
37/// Exclusions are validated at the start of every check: unassigned IDs, IDs absent from the view,
38/// self-references, and out-of-range robot shape indices fail the check and leave the report
39/// unmodified.
40///
41/// [`CuContactKind::SelfCollision`]: crate::CuContactKind::SelfCollision
42/// [`CuContactKind::EnvironmentCollision`]: crate::CuContactKind::EnvironmentCollision
43#[derive(Debug, Clone, Copy, Default)]
44pub struct CuCollisionEnv<'a> {
45    obstacles: &'a [CuCollisionShape],
46    /// Either empty (anonymous obstacles) or parallel to `obstacles`.
47    obstacle_ids: &'a [CuObjectId],
48    attached: &'a [CuAttachedShape],
49    exclusions: &'a [CuSceneExclusion],
50}
51
52impl<'a> CuCollisionEnv<'a> {
53    /// No obstacles, attached shapes, or exclusions: self-collision only.
54    pub const EMPTY: CuCollisionEnv<'static> = CuCollisionEnv {
55        obstacles: &[],
56        obstacle_ids: &[],
57        attached: &[],
58        exclusions: &[],
59    };
60
61    /// Environment of anonymous world-frame obstacles.
62    pub const fn new(obstacles: &'a [CuCollisionShape]) -> Self {
63        Self {
64            obstacles,
65            obstacle_ids: &[],
66            attached: &[],
67            exclusions: &[],
68        }
69    }
70
71    /// Obstacles with stable IDs; `ids` must be parallel to `obstacles`.
72    pub(crate) const fn with_obstacle_ids(
73        obstacles: &'a [CuCollisionShape],
74        obstacle_ids: &'a [CuObjectId],
75    ) -> Self {
76        Self {
77            obstacles,
78            obstacle_ids,
79            attached: &[],
80            exclusions: &[],
81        }
82    }
83
84    /// Adds shapes attached to robot frames (or the base frame).
85    pub const fn with_attached(mut self, attached: &'a [CuAttachedShape]) -> Self {
86        self.attached = attached;
87        self
88    }
89
90    /// Adds stable-ID pair exclusions for the attached shapes.
91    pub fn with_exclusions<const MAX_EXCLUSIONS: usize>(
92        mut self,
93        exclusions: &'a CuSceneExclusions<MAX_EXCLUSIONS>,
94    ) -> Self {
95        self.exclusions = exclusions.as_slice();
96        self
97    }
98
99    /// World-frame obstacle shapes.
100    pub const fn obstacles(&self) -> &'a [CuCollisionShape] {
101        self.obstacles
102    }
103
104    /// Attached shapes.
105    pub const fn attached(&self) -> &'a [CuAttachedShape] {
106        self.attached
107    }
108
109    /// Stable-ID exclusions.
110    pub const fn exclusions(&self) -> &'a [CuSceneExclusion] {
111        self.exclusions
112    }
113
114    pub(crate) fn obstacle_id(&self, index: usize) -> Option<CuObjectId> {
115        self.obstacle_ids.get(index).copied()
116    }
117
118    pub(crate) fn find_attached(&self, id: CuObjectId) -> bool {
119        self.attached.iter().any(|a| a.id == id)
120    }
121
122    pub(crate) fn find_obstacle(&self, id: CuObjectId) -> bool {
123        self.obstacle_ids.contains(&id)
124    }
125
126    /// `true` if the pair (attached `attached_id`, `partner`) is excluded, in either order for
127    /// attached-vs-attached pairs.
128    pub(crate) fn is_excluded(
129        &self,
130        attached_id: CuObjectId,
131        partner: CuSceneExclusionPartner,
132    ) -> bool {
133        self.exclusions.iter().any(|e| {
134            (e.attached_id == attached_id && e.partner == partner)
135                || matches!(partner, CuSceneExclusionPartner::Attached { attached_id: other }
136                    if e.attached_id == other
137                        && e.partner == CuSceneExclusionPartner::Attached { attached_id })
138        })
139    }
140}