1pub use parry3d as parry;
4
5use std::collections::hash_map::IntoIter;
8use std::collections::HashMap;
9use std::iter::FromIterator;
10
11use nalgebra::storage::Storage;
12use nalgebra::{Const, Vector};
13use parry::bounding_volume::{BoundingVolume, AABB};
14use parry::math::{Isometry, Point, Real, DIM};
15use parry::na::Unit;
16use parry::query::intersection_test;
17use parry::shape::{
18 Ball, Capsule, Compound, Cone, ConvexPolyhedron, Cuboid, Cylinder, HalfSpace,
19 RoundCone, RoundConvexPolyhedron, RoundCuboid, RoundCylinder, RoundTriangle,
20 Segment, Shape, SharedShape, Triangle,
21};
22use serde::{Deserialize, Serialize};
23
24use crate::error::{Error, Result};
25use crate::params::FromParams;
26
27use super::params::{Obstacle3dParams, Offset3dParams, Shape3dParams};
28use super::{AppearingObstacle, Behavior, Obstacle, ObstacleId, ObstacleSpace};
29
30pub type X = Real;
31pub const D: usize = DIM;
32pub type O = Obstacle3df32;
33pub type OS = ObstacleSpace3df32;
34
35#[derive(Clone, Serialize, Deserialize)]
37#[serde(
38 from = "Vec<(ObstacleId, Obstacle3df32)>",
39 into = "Vec<(ObstacleId, Obstacle3df32)>"
40)]
41pub struct ObstacleSpace3df32 {
42 obstacles: HashMap<ObstacleId, Obstacle3df32>,
43}
44
45impl ObstacleSpace3df32 {
46 fn internal_empty() -> Self {
47 Self {
48 obstacles: HashMap::new(),
49 }
50 }
51
52 fn internal_add_obstacles<I>(&mut self, obstacles: I)
53 where
54 I: IntoIterator<Item = (ObstacleId, Obstacle3df32)>,
55 {
56 obstacles.into_iter().for_each(|(obs_id, obs)| {
57 self.obstacles.insert(obs_id, obs);
58 })
59 }
60
61 fn internal_get_obstacles(
62 &self,
63 obstacles: &[ObstacleId],
64 ) -> Vec<&Obstacle3df32> {
65 obstacles
66 .iter()
67 .filter_map(|obs_id| self.obstacles.get(obs_id))
68 .collect()
69 }
70
71 fn internal_get_sensed_obstacles(&self) -> Vec<(ObstacleId, &Obstacle3df32)> {
72 self
73 .obstacles
74 .iter()
75 .filter(|(_id, obs)| obs.sensed)
76 .map(|(id, obs)| (*id, obs))
77 .collect()
78 }
79
80 fn internal_get_not_sensed_obstacles(
81 &self,
82 ) -> Vec<(ObstacleId, &Obstacle3df32)> {
83 self
84 .obstacles
85 .iter()
86 .filter(|(_id, obs)| !obs.sensed)
87 .map(|(id, obs)| (*id, obs))
88 .collect()
89 }
90
91 fn internal_get_all_obstacle_ids(&self) -> Vec<ObstacleId> {
92 self.obstacles.keys().cloned().collect()
93 }
94
95 fn internal_remove_obstacles(&mut self, obstacles: &[ObstacleId]) {
96 obstacles.iter().for_each(|obs_id| {
97 self.obstacles.remove(obs_id);
98 })
99 }
100
101 fn internal_count(&self) -> usize {
102 self.obstacles.len()
103 }
104
105 fn internal_check_sensors<S>(
106 &mut self,
107 pose: &Vector<X, Const<D>, S>,
108 radius: X,
109 ) -> Vec<ObstacleId>
110 where
111 S: Storage<X, Const<D>>,
112 {
113 let ball = Ball::new(radius);
115 let offset = Point::from(pose.clone_owned()).into();
116
117 let mut added = Vec::new();
118 for obs in self.obstacles.iter_mut() {
119 if !obs.1.sensed {
120 if obs.1.intersects_shape(&offset, &ball) {
121 obs.1.sensed = true;
122 added.push(*obs.0);
123 }
124 }
125 }
126 added
127 }
128}
129
130impl FromParams for ObstacleSpace3df32 {
131 type Params = Vec<<Obstacle3df32 as FromParams>::Params>;
132
133 fn from_params(params: Self::Params) -> Result<Self> {
134 let mut obs = Vec::new();
135 for obs_params in params {
136 obs.push(Obstacle3df32::from_params(obs_params)?);
137 }
138
139 Ok(obs.into_iter().collect())
140 }
141}
142
143impl FromIterator<(ObstacleId, Obstacle3df32)> for ObstacleSpace3df32 {
144 fn from_iter<I>(iter: I) -> Self
145 where
146 I: IntoIterator<Item = (ObstacleId, Obstacle3df32)>,
147 {
148 Self {
149 obstacles: HashMap::from_iter(iter),
150 }
151 }
152}
153
154impl FromIterator<Obstacle3df32> for ObstacleSpace3df32 {
155 fn from_iter<I>(iter: I) -> Self
156 where
157 I: IntoIterator<Item = Obstacle3df32>,
158 {
159 Self {
160 obstacles: HashMap::from_iter(iter.into_iter().enumerate()),
161 }
162 }
163}
164
165impl IntoIterator for ObstacleSpace3df32 {
166 type Item = (ObstacleId, Obstacle3df32);
167 type IntoIter = IntoIter<ObstacleId, Obstacle3df32>;
168 fn into_iter(self) -> Self::IntoIter {
169 self.obstacles.into_iter()
170 }
171}
172
173impl FromIterator<(Isometry<Real>, SharedShape)> for ObstacleSpace3df32 {
174 fn from_iter<I>(iter: I) -> Self
175 where
176 I: IntoIterator<Item = (Isometry<Real>, SharedShape)>,
177 {
178 Self {
179 obstacles: HashMap::from_iter(
180 iter.into_iter().map(|obs| obs.into()).enumerate(),
181 ),
182 }
183 }
184}
185
186impl From<Vec<(Isometry<Real>, SharedShape)>> for ObstacleSpace3df32 {
187 fn from(static_obstacles: Vec<(Isometry<Real>, SharedShape)>) -> Self {
188 static_obstacles.into_iter().collect()
189 }
190}
191
192impl From<Vec<Obstacle3df32>> for ObstacleSpace3df32 {
193 fn from(static_obstacles: Vec<Obstacle3df32>) -> Self {
194 static_obstacles.into_iter().collect()
195 }
196}
197
198impl From<Vec<(ObstacleId, Obstacle3df32)>> for ObstacleSpace3df32 {
199 fn from(static_obstacles: Vec<(ObstacleId, Obstacle3df32)>) -> Self {
200 static_obstacles.into_iter().collect()
201 }
202}
203
204impl Into<Vec<(ObstacleId, Obstacle3df32)>> for ObstacleSpace3df32 {
205 fn into(self) -> Vec<(ObstacleId, Obstacle3df32)> {
206 self.into_iter().collect()
207 }
208}
209
210mod euclidean {
211 use crate::cspace::{CSpace, EuclideanSpace};
212 use crate::trajectories::FullTrajectory;
213
214 use super::*;
215
216 const N: usize = D;
217 type CS = EuclideanSpace<X, N>;
218
219 impl Obstacle<X, CS, N> for ObstacleSpace3df32 {
220 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
221 where
222 TF: FullTrajectory<X, <CS as CSpace<X, N>>::Traj, S1, S2, N>,
223 S1: Storage<X, Const<N>>,
224 S2: Storage<X, Const<N>>,
225 {
226 let start = Point::from(t.start().clone_owned());
227 let end = Point::from(t.end().clone_owned());
228 let segment = Segment::new(start, end);
229 let identity = Isometry::identity();
230
231 self
232 .obstacles
233 .values()
234 .filter(|obs| obs.sensed)
235 .all(|obs| !obs.intersects_shape(&identity, &segment))
236 }
237
238 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
239 where
240 S: Storage<X, Const<N>>,
241 {
242 let x = Point::from(x.clone_owned());
243 self
244 .obstacles
245 .values()
246 .filter(|obs| obs.sensed)
247 .all(|obs| !obs.contains_point(&x))
248 }
249 }
250
251 impl ObstacleSpace<X, CS, N> for ObstacleSpace3df32 {
252 type Obs = Obstacle3df32;
253
254 fn empty() -> Self {
255 Self::internal_empty()
256 }
257
258 fn add_obstacles<I>(&mut self, obstacles: I)
259 where
260 I: IntoIterator<Item = (ObstacleId, Self::Obs)>,
261 {
262 self.internal_add_obstacles(obstacles)
263 }
264
265 fn get_obstacles(&self, obstacles: &[ObstacleId]) -> Vec<&Self::Obs> {
266 self.internal_get_obstacles(obstacles)
267 }
268
269 fn get_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
270 self.internal_get_sensed_obstacles()
271 }
272
273 fn get_not_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
274 self.internal_get_not_sensed_obstacles()
275 }
276
277 fn get_all_obstacle_ids(&self) -> Vec<ObstacleId> {
278 self.internal_get_all_obstacle_ids()
279 }
280
281 fn remove_obstacles(&mut self, obstacles: &[ObstacleId]) {
282 self.internal_remove_obstacles(obstacles)
283 }
284
285 fn count(&self) -> usize {
286 self.internal_count()
287 }
288
289 fn check_sensors<S>(
290 &mut self,
291 pose: &Vector<X, Const<N>, S>,
292 radius: X,
293 ) -> Vec<ObstacleId>
294 where
295 S: Storage<X, Const<N>>,
296 {
297 self.internal_check_sensors(pose, radius)
298 }
299 }
300
301 impl Obstacle<X, CS, N> for Obstacle3df32 {
302 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
303 where
304 TF: FullTrajectory<X, <CS as CSpace<X, N>>::Traj, S1, S2, N>,
305 S1: Storage<X, Const<N>>,
306 S2: Storage<X, Const<N>>,
307 {
308 let start = Point::from(t.start().clone_owned());
309 let end = Point::from(t.end().clone_owned());
310 let segment = Segment::new(start, end);
311 let identity = Isometry::identity();
312
313 !self.intersects_shape(&identity, &segment)
314 }
315
316 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
317 where
318 S: Storage<X, Const<N>>,
319 {
320 let x = Point::from(x.clone_owned());
321 !self.contains_point(&x)
322 }
323 }
324
325 impl AppearingObstacle<X, CS, N> for Obstacle3df32 {
326 fn can_sense<S>(&self, pose: &Vector<X, Const<N>, S>, radius: X) -> bool
327 where
328 S: Storage<X, Const<N>>,
329 {
330 if self.sensed {
331 return true;
332 }
333
334 let ball = Ball::new(radius);
335 let offset = Point::from(pose.clone_owned()).into();
336
337 self.intersects_shape(&offset, &ball)
338 }
339 }
340}
341
342mod leader_follower {
343 use crate::cspace::{leader_follower::LeaderFollowerCSpace, CSpace};
344 use crate::trajectories::FullTrajectory;
345
346 use super::*;
347
348 const N: usize = D * 2;
349
350 impl<CS> Obstacle<X, CS, N> for ObstacleSpace3df32
351 where
352 CS: LeaderFollowerCSpace<X, D, N>,
353 {
354 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
355 where
356 TF: FullTrajectory<X, CS::Traj, S1, S2, N>,
357 S1: Storage<X, Const<N>>,
358 S2: Storage<X, Const<N>>,
359 {
360 let lead_start = CS::get_leader(t.start());
362 let lead_end = CS::get_leader(t.end());
363 let fol_start = CS::get_follower(t.start());
364 let fol_end = CS::get_follower(t.end());
365
366 let identity = Isometry::identity();
367
368 let lead_traj = Segment::new(
370 Point::from(lead_start.clone_owned()),
371 Point::from(lead_end.clone_owned()),
372 );
373 let fol_traj = Segment::new(
374 Point::from(fol_start.clone_owned()),
375 Point::from(fol_end.clone_owned()),
376 );
377 let los_start = Segment::new(
378 Point::from(lead_start.clone_owned()),
379 Point::from(fol_start.clone_owned()),
380 );
381 let los_end = Segment::new(
382 Point::from(lead_end.clone_owned()),
383 Point::from(fol_end.clone_owned()),
384 );
385 let segments = [lead_traj, fol_traj, los_start, los_end];
386 self.obstacles.values().filter(|obs| obs.sensed).all(|obs| {
387 segments
388 .iter()
389 .all(|seg| !obs.intersects_shape(&identity, seg))
390 })
391 }
392
393 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
394 where
395 S: Storage<X, Const<N>>,
396 {
397 let leader = CS::get_leader(x);
399 let follower = CS::get_follower(x);
400
401 let identity = Isometry::identity();
402
403 let segment = Segment::new(
405 Point::from(leader.clone_owned()),
406 Point::from(follower.clone_owned()),
407 );
408
409 self
410 .obstacles
411 .values()
412 .filter(|obs| obs.sensed)
413 .all(|obs| !obs.intersects_shape(&identity, &segment))
414 }
415 }
416
417 impl<CS> ObstacleSpace<X, CS, N> for ObstacleSpace3df32
418 where
419 CS: LeaderFollowerCSpace<X, D, N>,
420 {
421 type Obs = Obstacle3df32;
422
423 fn empty() -> Self {
424 Self::internal_empty()
425 }
426
427 fn add_obstacles<I>(&mut self, obstacles: I)
428 where
429 I: IntoIterator<Item = (ObstacleId, Self::Obs)>,
430 {
431 self.internal_add_obstacles(obstacles)
432 }
433
434 fn get_obstacles(&self, obstacles: &[ObstacleId]) -> Vec<&Self::Obs> {
435 self.internal_get_obstacles(obstacles)
436 }
437
438 fn get_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
439 self.internal_get_sensed_obstacles()
440 }
441
442 fn get_not_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
443 self.internal_get_not_sensed_obstacles()
444 }
445
446 fn get_all_obstacle_ids(&self) -> Vec<ObstacleId> {
447 self.internal_get_all_obstacle_ids()
448 }
449
450 fn remove_obstacles(&mut self, obstacles: &[ObstacleId]) {
451 self.internal_remove_obstacles(obstacles)
452 }
453
454 fn count(&self) -> usize {
455 self.internal_count()
456 }
457
458 fn check_sensors<S>(
459 &mut self,
460 pose: &Vector<X, Const<N>, S>,
461 radius: X,
462 ) -> Vec<ObstacleId>
463 where
464 S: Storage<X, Const<N>>,
465 {
466 let leader = CS::get_leader(pose);
467 self.internal_check_sensors(&leader, radius)
468 }
469 }
470
471 impl<CS> Obstacle<X, CS, N> for Obstacle3df32
472 where
473 CS: LeaderFollowerCSpace<X, D, N>,
474 {
475 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
476 where
477 TF: FullTrajectory<X, <CS as CSpace<X, N>>::Traj, S1, S2, N>,
478 S1: Storage<X, Const<N>>,
479 S2: Storage<X, Const<N>>,
480 {
481 let lead_start = CS::get_leader(t.start());
483 let lead_end = CS::get_leader(t.end());
484 let fol_start = CS::get_follower(t.start());
485 let fol_end = CS::get_follower(t.end());
486
487 let identity = Isometry::identity();
488
489 let lead_traj = Segment::new(
491 Point::from(lead_start.clone_owned()),
492 Point::from(lead_end.clone_owned()),
493 );
494 let fol_traj = Segment::new(
495 Point::from(fol_start.clone_owned()),
496 Point::from(fol_end.clone_owned()),
497 );
498 let los_start = Segment::new(
499 Point::from(lead_start.clone_owned()),
500 Point::from(fol_start.clone_owned()),
501 );
502 let los_end = Segment::new(
503 Point::from(lead_end.clone_owned()),
504 Point::from(fol_end.clone_owned()),
505 );
506 let segments = [lead_traj, fol_traj, los_start, los_end];
507 segments
508 .iter()
509 .all(|seg| !self.intersects_shape(&identity, seg))
510 }
511
512 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
513 where
514 S: Storage<X, Const<N>>,
515 {
516 let leader = CS::get_leader(x);
518 let follower = CS::get_follower(x);
519
520 let identity = Isometry::identity();
521
522 let segment = Segment::new(
524 Point::from(leader.clone_owned()),
525 Point::from(follower.clone_owned()),
526 );
527
528 !self.intersects_shape(&identity, &segment)
529 }
530 }
531
532 impl<CS> AppearingObstacle<X, CS, N> for Obstacle3df32
533 where
534 CS: LeaderFollowerCSpace<X, D, N>,
535 {
536 fn can_sense<S>(&self, pose: &Vector<X, Const<N>, S>, radius: X) -> bool
537 where
538 S: Storage<X, Const<N>>,
539 {
540 if self.sensed {
541 return true;
542 }
543
544 let leader = CS::get_leader(pose);
545 let ball = Ball::new(radius);
546 let offset = Point::from(leader.clone_owned()).into();
547
548 self.intersects_shape(&offset, &ball)
549 }
550 }
551}
552
553#[derive(Clone, Serialize, Deserialize)]
556pub struct Obstacle3df32 {
557 shape: SharedShape,
558 offset: Isometry<X>,
559 sensed: bool,
560 aabb: AABB,
561}
562
563impl Obstacle3df32 {
564 pub fn no_offset(shape: SharedShape) -> Self {
565 Self::with_offset(shape, Isometry::identity())
566 }
567
568 pub fn with_offset(shape: SharedShape, offset: Isometry<X>) -> Self {
569 Self::new(shape, offset, Behavior::default() == Behavior::Static)
570 }
571
572 pub fn new(shape: SharedShape, offset: Isometry<X>, sensed: bool) -> Self {
573 let aabb = shape.compute_aabb(&offset);
574 Self {
575 shape,
576 offset,
577 sensed,
578 aabb,
579 }
580 }
581
582 pub fn shape(&self) -> &SharedShape {
583 &self.shape
584 }
585
586 pub fn offset(&self) -> &Isometry<X> {
587 &self.offset
588 }
589
590 pub fn sensed(&self) -> bool {
591 self.sensed
592 }
593
594 pub fn aabb(&self) -> &AABB {
595 &self.aabb
596 }
597
598 pub fn intersects(&self, other: &Self) -> bool {
599 if !self.aabb.intersects(&other.aabb) {
600 return false;
601 }
602
603 intersection_test(
604 &self.offset,
605 self.shape.0.as_ref(),
606 &other.offset,
607 other.shape.0.as_ref(),
608 )
609 .unwrap()
610 }
611
612 fn intersects_shape<S: Shape>(
613 &self,
614 offset: &Isometry<X>,
615 shape: &S,
616 ) -> bool {
617 if !self.aabb.intersects(&shape.compute_aabb(offset)) {
618 return false;
619 }
620
621 intersection_test(&self.offset, self.shape.0.as_ref(), offset, shape)
622 .unwrap()
623 }
624
625 fn contains_point(&self, point: &Point<X>) -> bool {
626 if !self.aabb.contains_local_point(point) {
627 return false;
628 }
629
630 self.shape.contains_point(&self.offset, point)
631 }
632}
633
634impl FromParams for Obstacle3df32 {
635 type Params = Obstacle3dParams<X>;
636 fn from_params(params: Self::Params) -> Result<Self> {
637 let sensed = match params.behavior {
638 Behavior::Static => true,
639 Behavior::Appearing => false,
640 };
641
642 Ok(Obstacle3df32::new(
643 SharedShape::from_params(params.shape)?,
644 Isometry::from_params(params.offset)?,
645 sensed,
646 ))
647 }
648}
649
650impl From<(Isometry<Real>, SharedShape)> for Obstacle3df32 {
651 fn from(static_obstacle: (Isometry<Real>, SharedShape)) -> Self {
652 Self::with_offset(static_obstacle.1, static_obstacle.0)
653 }
654}
655
656impl FromParams for Isometry<X> {
657 type Params = Offset3dParams<X>;
658 fn from_params(params: Self::Params) -> Result<Self> {
659 Ok(Isometry::new(
660 params.translation,
661 params.axis_angle.map(|x| x.to_radians()),
662 ))
663 }
664}
665
666impl FromParams for SharedShape {
667 type Params = Shape3dParams<X>;
668 fn from_params(params: Self::Params) -> Result<Self> {
669 match params {
670 Shape3dParams::Ball { radius } => Ok(SharedShape::new(Ball::new(radius))),
671 Shape3dParams::Capsule { a, b, radius } => {
672 Ok(SharedShape::new(Capsule::new(a.into(), b.into(), radius)))
673 }
674 Shape3dParams::Compound { shapes } => {
675 let mut new_shapes = Vec::new();
676 for (offset, shape) in shapes {
677 new_shapes.push((
678 Isometry::from_params(offset)?,
679 SharedShape::from_params(shape)?,
680 ))
681 }
682 Ok(SharedShape::new(Compound::new(new_shapes)))
683 }
684 Shape3dParams::ConvexPolyhedron { points } => {
685 let points: Vec<_> = points.into_iter().map(|p| p.into()).collect();
686 let poly = ConvexPolyhedron::from_convex_hull(&points)
687 .ok_or(Error::ConvexHullError)?;
688 Ok(SharedShape::new(poly))
689 }
690 Shape3dParams::Cone {
691 half_height,
692 radius,
693 } => Ok(SharedShape::new(Cone::new(half_height, radius))),
694 Shape3dParams::Cuboid { half_extents } => {
695 Ok(SharedShape::new(Cuboid::new(half_extents)))
696 }
697 Shape3dParams::Cylinder {
698 half_height,
699 radius,
700 } => Ok(SharedShape::new(Cylinder::new(half_height, radius))),
701 Shape3dParams::HalfSpace { normal } => Ok(SharedShape::new(
702 HalfSpace::new(Unit::new_normalize(normal)),
703 )),
704 Shape3dParams::RoundCone {
705 half_height,
706 radius,
707 border_radius,
708 } => {
709 let cone = Cone::new(half_height, radius);
710 let cone = RoundCone {
711 base_shape: cone,
712 border_radius: border_radius,
713 };
714 Ok(SharedShape::new(cone))
715 }
716 Shape3dParams::RoundConvexPolyhedron {
717 points,
718 border_radius,
719 } => {
720 let points: Vec<_> = points.into_iter().map(|p| p.into()).collect();
721 let poly = ConvexPolyhedron::from_convex_hull(&points)
722 .ok_or(Error::ConvexHullError)?;
723 let poly = RoundConvexPolyhedron {
724 base_shape: poly,
725 border_radius: border_radius,
726 };
727 Ok(SharedShape::new(poly))
728 }
729 Shape3dParams::RoundCylinder {
730 half_height,
731 radius,
732 border_radius,
733 } => {
734 let cylinder = Cylinder::new(half_height, radius);
735 let cylinder = RoundCylinder {
736 base_shape: cylinder,
737 border_radius: border_radius,
738 };
739 Ok(SharedShape::new(cylinder))
740 }
741 Shape3dParams::RoundCuboid {
742 half_extents,
743 border_radius,
744 } => {
745 let cube = Cuboid::new(half_extents);
746 let cube = RoundCuboid {
747 base_shape: cube,
748 border_radius: border_radius,
749 };
750 Ok(SharedShape::new(cube))
751 }
752 Shape3dParams::RoundTriangle {
753 a,
754 b,
755 c,
756 border_radius,
757 } => {
758 let tri = Triangle::new(a.into(), b.into(), c.into());
759 let tri = RoundTriangle {
760 base_shape: tri,
761 border_radius: border_radius,
762 };
763 Ok(SharedShape::new(tri))
764 }
765 Shape3dParams::Segment { a, b } => {
766 Ok(SharedShape::new(Segment::new(a.into(), b.into())))
767 }
768 Shape3dParams::Triangle { a, b, c } => Ok(SharedShape::new(
769 Triangle::new(a.into(), b.into(), c.into()),
770 )),
771 }
772 }
773}
774
775#[cfg(test)]
776mod tests {
777
778 use parry::na::base::Unit;
779 use parry::shape::{
780 Ball, Capsule, Cone, ConvexPolyhedron, Cuboid, Cylinder, HalfSpace,
781 RoundCone, RoundConvexPolyhedron, RoundCuboid, RoundCylinder,
782 RoundTriangle, Segment, SharedShape, Triangle,
783 };
784
785 use crate::trajectories::{EuclideanTrajectory, FullTrajOwned};
786
787 use super::*;
788
789 fn get_shapes() -> Vec<(Isometry<Real>, SharedShape)> {
790 vec![
791 SharedShape::new(Ball::new(0.5)),
792 SharedShape::new(Capsule::new(
793 [0.0, 0.0, 0.0].into(),
794 [0.0, 1.0, 0.0].into(),
795 0.5,
796 )),
797 SharedShape::new(Cone::new(0.5, 1.0)),
799 SharedShape::new(
800 ConvexPolyhedron::from_convex_hull(&[
801 [0.5, 0.0, 0.5].into(),
802 [-0.5, 0.0, 0.5].into(),
803 [0.0, 1.0, 0.0].into(),
804 [0.0, 0.1, 0.0].into(),
805 [0.0, 0.0, 1.0].into(),
806 ])
807 .unwrap(),
808 ),
809 SharedShape::new(Cuboid::new([0.5, 0.5, 0.5].into())),
810 SharedShape::new(Cylinder::new(0.5, 1.0)),
811 SharedShape::new(HalfSpace::new(Unit::new_normalize(
812 [1.0, 1.0, 1.0].into(),
813 ))),
814 SharedShape::new(RoundCone {
816 base_shape: Cone::new(0.5, 1.0),
817 border_radius: 0.1,
818 }),
819 SharedShape::new(RoundConvexPolyhedron {
820 base_shape: ConvexPolyhedron::from_convex_hull(&[
821 [0.5, 0.0, 0.5].into(),
822 [-0.5, 0.0, 0.5].into(),
823 [0.0, 1.0, 0.0].into(),
824 [0.0, 0.1, 0.0].into(),
825 [0.0, 0.0, 1.0].into(),
826 ])
827 .unwrap(),
828 border_radius: 0.1,
829 }),
830 SharedShape::new(RoundCuboid {
831 base_shape: Cuboid::new([0.5, 0.5, 0.5].into()),
832 border_radius: 0.1,
833 }),
834 SharedShape::new(RoundCylinder {
835 base_shape: Cylinder::new(0.5, 1.0),
836 border_radius: 0.1,
837 }),
838 SharedShape::new(RoundTriangle {
839 base_shape: Triangle::new(
840 [0.5, 0.0, 0.0].into(),
841 [-0.5, 0.0, 0.0].into(),
842 [0.0, 1.0, 1.0].into(),
843 ),
844 border_radius: 0.1,
845 }),
846 SharedShape::new(Segment::new(
847 [0.0, 0.0, 0.0].into(),
848 [1.0, 1.0, 1.0].into(),
849 )),
850 SharedShape::new(Triangle::new(
852 [0.5, 0.0, 0.0].into(),
853 [-0.5, 0.0, 0.0].into(),
854 [0.0, 1.0, 1.0].into(),
855 )),
856 ]
857 .into_iter()
858 .map(|i| (Isometry::<X>::identity(), i))
859 .collect()
860 }
861
862 fn get_obstacle_space() -> ObstacleSpace3df32 {
863 ObstacleSpace3df32::from(get_shapes())
864 }
865
866 fn get_obstacles() -> Vec<Obstacle3df32> {
867 get_shapes().into_iter().map(Obstacle3df32::from).collect()
868 }
869
870 #[test]
871 fn test_serialize_bincode_obstacles() {
872 let obstacles = get_obstacles();
873 let v = bincode::serialize(&obstacles).unwrap();
874 let _: Vec<Obstacle3df32> = bincode::deserialize(&v).unwrap();
875 }
876
877 #[test]
878 fn test_intersection_parry_obstacle() {
879 let obstacle_space = get_obstacle_space();
880 let t = FullTrajOwned::new(
881 [0.0, 0.0, 0.0].into(),
882 [0.0, 1.0, 0.0].into(),
883 EuclideanTrajectory::new(),
884 );
885 assert_eq!(obstacle_space.trajectory_free(&t), false);
886
887 let t = FullTrajOwned::new(
888 [1.0, 10.0, 0.0].into(),
889 [0.0, 10.0, 0.0].into(),
890 EuclideanTrajectory::new(),
891 );
892 assert_eq!(obstacle_space.trajectory_free(&t), true);
893 }
894
895 #[test]
896 fn test_intersection_ball() {
897 let ball = Ball::new(0.75);
898 let ball = Obstacle3df32::no_offset(SharedShape::new(ball));
899 let t = FullTrajOwned::new(
900 [0.0, 0.0, 0.0].into(),
901 [0.0, 1.0, 0.0].into(),
902 EuclideanTrajectory::new(),
903 );
904 assert_eq!(ball.trajectory_free(&t), false);
905 let t = FullTrajOwned::new(
906 [1.0, 0.0, 0.0].into(),
907 [0.0, 1.0, 0.0].into(),
908 EuclideanTrajectory::new(),
909 );
910 assert_eq!(ball.trajectory_free(&t), false);
911 let t = FullTrajOwned::new(
912 [-1.0, 0.0, 0.0].into(),
913 [1.0, 0.0, 0.0].into(),
914 EuclideanTrajectory::new(),
915 );
916 assert_eq!(ball.trajectory_free(&t), false);
917 let t = FullTrajOwned::new(
918 [1.0, -1.0, 0.0].into(),
919 [1.0, 1.0, 0.0].into(),
920 EuclideanTrajectory::new(),
921 );
922 assert_eq!(ball.trajectory_free(&t), true);
923 let t = FullTrajOwned::new(
924 [0.7500001, -1.0, 0.0].into(),
925 [0.7500001, 1.0, 0.0].into(),
926 EuclideanTrajectory::new(),
927 );
928 assert_eq!(ball.trajectory_free(&t), true);
929 }
930}