1pub use parry2d as parry;
4
5use std::collections::hash_map::IntoIter;
8use std::collections::HashMap;
9use std::f32::consts::TAU;
10use std::iter::FromIterator;
11
12use nalgebra::storage::Storage;
13use nalgebra::{Const, Vector};
14use parry::bounding_volume::{BoundingVolume, AABB};
15use parry::math::{Isometry, Point, Real, Rotation, DIM};
16use parry::na::Unit;
17use parry::query::intersection_test;
18use parry::shape::{
19 Ball, Capsule, Compound, ConvexPolygon, Cuboid, HalfSpace,
20 RoundConvexPolygon, RoundCuboid, RoundTriangle, Segment, Shape, SharedShape,
21 Triangle,
22};
23use serde::{Deserialize, Serialize};
24
25use crate::cspace::leader_follower::LeaderFollowerCSpace;
26use crate::cspace::{CSpace, EuclideanSpace};
27use crate::error::{Error, Result};
28use crate::params::FromParams;
29use crate::trajectories::FullTrajectory;
30
31use super::params::{Obstacle2dParams, Offset2dParams, Shape2dParams};
32use super::{AppearingObstacle, Behavior, Obstacle, ObstacleId, ObstacleSpace};
33
34pub type X = Real;
35pub const D: usize = DIM;
36pub type O = Obstacle2df32;
37pub type OS = ObstacleSpace2df32;
38
39#[derive(Clone, Serialize, Deserialize)]
41#[serde(
42 from = "Vec<(ObstacleId, Obstacle2df32)>",
43 into = "Vec<(ObstacleId, Obstacle2df32)>"
44)]
45pub struct ObstacleSpace2df32 {
46 obstacles: HashMap<ObstacleId, Obstacle2df32>,
47}
48
49impl ObstacleSpace2df32 {
50 fn internal_empty() -> Self {
51 Self {
52 obstacles: HashMap::new(),
53 }
54 }
55
56 fn internal_add_obstacles<I>(&mut self, obstacles: I)
57 where
58 I: IntoIterator<Item = (ObstacleId, Obstacle2df32)>,
59 {
60 obstacles.into_iter().for_each(|(obs_id, obs)| {
61 self.obstacles.insert(obs_id, obs);
62 })
63 }
64
65 fn internal_get_obstacles(
66 &self,
67 obstacles: &[ObstacleId],
68 ) -> Vec<&Obstacle2df32> {
69 obstacles
70 .iter()
71 .filter_map(|obs_id| self.obstacles.get(obs_id))
72 .collect()
73 }
74
75 fn internal_get_sensed_obstacles(&self) -> Vec<(ObstacleId, &Obstacle2df32)> {
76 self
77 .obstacles
78 .iter()
79 .filter(|(_id, obs)| obs.sensed)
80 .map(|(id, obs)| (*id, obs))
81 .collect()
82 }
83
84 fn internal_get_not_sensed_obstacles(
85 &self,
86 ) -> Vec<(ObstacleId, &Obstacle2df32)> {
87 self
88 .obstacles
89 .iter()
90 .filter(|(_id, obs)| !obs.sensed)
91 .map(|(id, obs)| (*id, obs))
92 .collect()
93 }
94
95 fn internal_get_all_obstacle_ids(&self) -> Vec<ObstacleId> {
96 self.obstacles.keys().cloned().collect()
97 }
98
99 fn internal_remove_obstacles(&mut self, obstacles: &[ObstacleId]) {
100 obstacles.iter().for_each(|obs_id| {
101 self.obstacles.remove(obs_id);
102 })
103 }
104
105 fn internal_count(&self) -> usize {
106 self.obstacles.len()
107 }
108
109 fn internal_check_sensors<S>(
110 &mut self,
111 pose: &Vector<X, Const<D>, S>,
112 radius: X,
113 ) -> Vec<ObstacleId>
114 where
115 S: Storage<X, Const<D>>,
116 {
117 let ball = Ball::new(radius);
119 let offset = Point::from(pose.clone_owned()).into();
120
121 let mut added = Vec::new();
122 for obs in self.obstacles.iter_mut() {
123 if !obs.1.sensed {
124 if obs.1.intersects_shape(&offset, &ball) {
125 obs.1.sensed = true;
126 added.push(*obs.0);
127 }
128 }
129 }
130 added
131 }
132}
133
134impl FromParams for ObstacleSpace2df32 {
135 type Params = Vec<<Obstacle2df32 as FromParams>::Params>;
136
137 fn from_params(params: Self::Params) -> Result<Self> {
138 let mut obs = Vec::new();
139 for obs_params in params {
140 obs.push(Obstacle2df32::from_params(obs_params)?);
141 }
142
143 Ok(obs.into_iter().collect())
144 }
145}
146
147impl FromIterator<(ObstacleId, Obstacle2df32)> for ObstacleSpace2df32 {
148 fn from_iter<I>(iter: I) -> Self
149 where
150 I: IntoIterator<Item = (ObstacleId, Obstacle2df32)>,
151 {
152 Self {
153 obstacles: HashMap::from_iter(iter),
154 }
155 }
156}
157
158impl FromIterator<Obstacle2df32> for ObstacleSpace2df32 {
159 fn from_iter<I>(iter: I) -> Self
160 where
161 I: IntoIterator<Item = Obstacle2df32>,
162 {
163 Self {
164 obstacles: HashMap::from_iter(iter.into_iter().enumerate()),
165 }
166 }
167}
168
169impl IntoIterator for ObstacleSpace2df32 {
170 type Item = (ObstacleId, Obstacle2df32);
171 type IntoIter = IntoIter<ObstacleId, Obstacle2df32>;
172 fn into_iter(self) -> Self::IntoIter {
173 self.obstacles.into_iter()
174 }
175}
176
177impl FromIterator<(Isometry<Real>, SharedShape)> for ObstacleSpace2df32 {
178 fn from_iter<I>(iter: I) -> Self
179 where
180 I: IntoIterator<Item = (Isometry<Real>, SharedShape)>,
181 {
182 Self {
183 obstacles: HashMap::from_iter(
184 iter.into_iter().map(|obs| obs.into()).enumerate(),
185 ),
186 }
187 }
188}
189
190impl From<Vec<(Isometry<Real>, SharedShape)>> for ObstacleSpace2df32 {
191 fn from(static_obstacles: Vec<(Isometry<Real>, SharedShape)>) -> Self {
192 static_obstacles.into_iter().collect()
193 }
194}
195
196impl From<Vec<Obstacle2df32>> for ObstacleSpace2df32 {
197 fn from(static_obstacles: Vec<Obstacle2df32>) -> Self {
198 static_obstacles.into_iter().collect()
199 }
200}
201
202impl From<Vec<(ObstacleId, Obstacle2df32)>> for ObstacleSpace2df32 {
203 fn from(static_obstacles: Vec<(ObstacleId, Obstacle2df32)>) -> Self {
204 static_obstacles.into_iter().collect()
205 }
206}
207
208impl Into<Vec<(ObstacleId, Obstacle2df32)>> for ObstacleSpace2df32 {
209 fn into(self) -> Vec<(ObstacleId, Obstacle2df32)> {
210 self.into_iter().collect()
211 }
212}
213
214mod euclidean {
215
216 use super::*;
217
218 const N: usize = D;
219 type CS = EuclideanSpace<X, N>;
220
221 impl Obstacle<X, CS, N> for ObstacleSpace2df32 {
222 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
223 where
224 TF: FullTrajectory<X, <CS as CSpace<X, N>>::Traj, S1, S2, N>,
225 S1: Storage<X, Const<N>>,
226 S2: Storage<X, Const<N>>,
227 {
228 let start = Point::from(t.start().clone_owned());
229 let end = Point::from(t.end().clone_owned());
230 let segment = Segment::new(start, end);
231 let identity = Isometry::identity();
232
233 self
234 .obstacles
235 .values()
236 .filter(|obs| obs.sensed)
237 .all(|obs| !obs.intersects_shape(&identity, &segment))
238 }
239
240 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
241 where
242 S: Storage<X, Const<N>>,
243 {
244 let x = Point::from(x.clone_owned());
245 self
246 .obstacles
247 .values()
248 .filter(|obs| obs.sensed)
249 .all(|obs| !obs.contains_point(&x))
250 }
251 }
252
253 impl ObstacleSpace<X, CS, N> for ObstacleSpace2df32 {
254 type Obs = Obstacle2df32;
255
256 fn empty() -> Self {
257 Self::internal_empty()
258 }
259
260 fn add_obstacles<I>(&mut self, obstacles: I)
261 where
262 I: IntoIterator<Item = (ObstacleId, Self::Obs)>,
263 {
264 self.internal_add_obstacles(obstacles)
265 }
266
267 fn get_obstacles(&self, obstacles: &[ObstacleId]) -> Vec<&Self::Obs> {
268 self.internal_get_obstacles(obstacles)
269 }
270
271 fn get_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
272 self.internal_get_sensed_obstacles()
273 }
274
275 fn get_not_sensed_obstacles(&self) -> Vec<(ObstacleId, &Self::Obs)> {
276 self.internal_get_not_sensed_obstacles()
277 }
278
279 fn get_all_obstacle_ids(&self) -> Vec<ObstacleId> {
280 self.internal_get_all_obstacle_ids()
281 }
282
283 fn remove_obstacles(&mut self, obstacles: &[ObstacleId]) {
284 self.internal_remove_obstacles(obstacles)
285 }
286
287 fn count(&self) -> usize {
288 self.internal_count()
289 }
290
291 fn check_sensors<S>(
292 &mut self,
293 pose: &Vector<X, Const<N>, S>,
294 radius: X,
295 ) -> Vec<ObstacleId>
296 where
297 S: Storage<X, Const<N>>,
298 {
299 self.internal_check_sensors(pose, radius)
300 }
301 }
302
303 impl Obstacle<X, CS, N> for Obstacle2df32 {
304 fn trajectory_free<TF, S1, S2>(&self, t: &TF) -> bool
305 where
306 TF: FullTrajectory<X, <CS as CSpace<X, N>>::Traj, S1, S2, N>,
307 S1: Storage<X, Const<N>>,
308 S2: Storage<X, Const<N>>,
309 {
310 let start = Point::from(t.start().clone_owned());
311 let end = Point::from(t.end().clone_owned());
312 let segment = Segment::new(start, end);
313 let identity = Isometry::identity();
314
315 !self.intersects_shape(&identity, &segment)
316 }
317
318 fn is_free<S>(&self, x: &Vector<X, Const<N>, S>) -> bool
319 where
320 S: Storage<X, Const<N>>,
321 {
322 let x = Point::from(x.clone_owned());
323 !self.contains_point(&x)
324 }
325 }
326
327 impl AppearingObstacle<X, CS, N> for Obstacle2df32 {
328 fn can_sense<S>(&self, pose: &Vector<X, Const<N>, S>, radius: X) -> bool
329 where
330 S: Storage<X, Const<N>>,
331 {
332 if self.sensed {
333 return true;
334 }
335
336 let ball = Ball::new(radius);
337 let offset = Point::from(pose.clone_owned()).into();
338
339 self.intersects_shape(&offset, &ball)
340 }
341 }
342}
343
344mod leader_follower {
345
346 use super::*;
347
348 const N: usize = D * 2;
349
350 impl<CS> Obstacle<X, CS, N> for ObstacleSpace2df32
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 ObstacleSpace2df32
418 where
419 CS: LeaderFollowerCSpace<X, D, N>,
420 {
421 type Obs = Obstacle2df32;
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 Obstacle2df32
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 Obstacle2df32
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 Obstacle2df32 {
557 shape: SharedShape,
558 offset: Isometry<X>,
559 sensed: bool,
560 aabb: AABB,
561}
562
563impl Obstacle2df32 {
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 Obstacle2df32 {
635 type Params = Obstacle2dParams<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(Obstacle2df32::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 Obstacle2df32 {
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 = Offset2dParams<X>;
658 fn from_params(params: Self::Params) -> Result<Self> {
659 Ok(Isometry::new(
660 params.translation,
661 X::to_radians(params.angle),
662 ))
663 }
664}
665
666fn create_regular_polygon(sides: u8, scale: X) -> Vec<Point<X>> {
667 let mut v = Vec::with_capacity(usize::from(sides));
668 let mut point = Point::new(scale, 0.0);
669 let rotation = Rotation::new(TAU / (sides as X));
670
671 for _ in 0..(sides) {
672 v.push(point.clone());
673 point = rotation * point;
674 }
675 v
676}
677
678impl FromParams for SharedShape {
679 type Params = Shape2dParams<X>;
680 fn from_params(params: Self::Params) -> Result<Self> {
681 match params {
682 Shape2dParams::Ball { radius } => Ok(SharedShape::new(Ball::new(radius))),
683 Shape2dParams::Capsule { a, b, radius } => {
684 Ok(SharedShape::new(Capsule::new(a.into(), b.into(), radius)))
685 }
686 Shape2dParams::Compound { shapes } => {
687 let mut new_shapes = Vec::new();
688 for (offset, shape) in shapes {
689 new_shapes.push((
690 Isometry::from_params(offset)?,
691 SharedShape::from_params(shape)?,
692 ))
693 }
694 Ok(SharedShape::new(Compound::new(new_shapes)))
695 }
696 Shape2dParams::RegularPolygon { sides, scale } => {
697 let points = create_regular_polygon(sides, scale);
698 let poly = ConvexPolygon::from_convex_hull(&points)
699 .ok_or(Error::ConvexHullError)?;
700 Ok(SharedShape::new(poly))
701 }
702 Shape2dParams::ConvexPolygon { points } => {
703 let points: Vec<_> = points.into_iter().map(|p| p.into()).collect();
704 let poly = ConvexPolygon::from_convex_hull(&points)
705 .ok_or(Error::ConvexHullError)?;
706 Ok(SharedShape::new(poly))
707 }
708 Shape2dParams::Cuboid { half_extents } => {
709 Ok(SharedShape::new(Cuboid::new(half_extents)))
710 }
711 Shape2dParams::HalfSpace { normal } => Ok(SharedShape::new(
712 HalfSpace::new(Unit::new_normalize(normal)),
713 )),
714 Shape2dParams::RoundRegularPolygon {
715 sides,
716 scale,
717 border_radius,
718 } => {
719 let points = create_regular_polygon(sides, scale);
720 let poly = ConvexPolygon::from_convex_hull(&points)
721 .ok_or(Error::ConvexHullError)?;
722 let poly = RoundConvexPolygon {
723 base_shape: poly,
724 border_radius: border_radius,
725 };
726 Ok(SharedShape::new(poly))
727 }
728 Shape2dParams::RoundConvexPolygon {
729 points,
730 border_radius,
731 } => {
732 let points: Vec<_> = points.into_iter().map(|p| p.into()).collect();
733 let poly = ConvexPolygon::from_convex_hull(&points)
734 .ok_or(Error::ConvexHullError)?;
735 let poly = RoundConvexPolygon {
736 base_shape: poly,
737 border_radius: border_radius,
738 };
739 Ok(SharedShape::new(poly))
740 }
741 Shape2dParams::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 Shape2dParams::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 Shape2dParams::Segment { a, b } => {
766 Ok(SharedShape::new(Segment::new(a.into(), b.into())))
767 }
768 Shape2dParams::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, ConvexPolygon, Cuboid, HalfSpace, RoundConvexPolygon,
781 RoundCuboid, RoundTriangle, Segment, SharedShape, Triangle,
782 };
783
784 use crate::trajectories::{EuclideanTrajectory, FullTrajOwned};
785
786 use super::*;
787
788 fn get_shapes() -> Vec<(Isometry<Real>, SharedShape)> {
789 vec![
790 SharedShape::new(Ball::new(0.5)),
791 SharedShape::new(Capsule::new([0.0, 0.0].into(), [0.0, 1.0].into(), 0.5)),
792 SharedShape::new(
794 ConvexPolygon::from_convex_hull(&[
795 [0.5, 0.0].into(),
796 [-0.5, 0.0].into(),
797 [0.0, 1.0].into(),
798 [0.0, 0.1].into(),
799 [0.0, 0.0].into(),
800 ])
801 .unwrap(),
802 ),
803 SharedShape::new(Cuboid::new([0.5, 0.5].into())),
804 SharedShape::new(HalfSpace::new(Unit::new_normalize([1.0, 1.0].into()))),
805 SharedShape::new(RoundConvexPolygon {
807 base_shape: ConvexPolygon::from_convex_hull(&[
808 [0.5, 0.0].into(),
809 [-0.5, 0.0].into(),
810 [0.0, 1.0].into(),
811 [0.0, 0.1].into(),
812 [0.0, 0.0].into(),
813 ])
814 .unwrap(),
815 border_radius: 0.1,
816 }),
817 SharedShape::new(RoundCuboid {
818 base_shape: Cuboid::new([0.5, 0.5].into()),
819 border_radius: 0.1,
820 }),
821 SharedShape::new(RoundTriangle {
822 base_shape: Triangle::new(
823 [0.5, 0.0].into(),
824 [-0.5, 0.0].into(),
825 [0.0, 1.0].into(),
826 ),
827 border_radius: 0.1,
828 }),
829 SharedShape::new(Segment::new([0.0, 0.0].into(), [1.0, 1.0].into())),
830 SharedShape::new(Triangle::new(
832 [0.5, 0.0].into(),
833 [-0.5, 0.0].into(),
834 [0.0, 1.0].into(),
835 )),
836 ]
837 .into_iter()
838 .map(|i| (Isometry::<X>::identity(), i))
839 .collect()
840 }
841
842 fn get_obstacle_space() -> ObstacleSpace2df32 {
843 ObstacleSpace2df32::from(get_shapes())
844 }
845
846 fn get_obstacles() -> Vec<Obstacle2df32> {
847 get_shapes().into_iter().map(Obstacle2df32::from).collect()
848 }
849
850 #[test]
851 fn test_serialize_bincode_obstacles() {
852 let obstacles = get_obstacles();
853 let v = bincode::serialize(&obstacles).unwrap();
854 let _: Vec<Obstacle2df32> = bincode::deserialize(&v).unwrap();
855 }
856
857 #[test]
858 fn test_intersection_parry_obstacle() {
859 let obstacle_space = get_obstacle_space();
860 let t = FullTrajOwned::new(
861 [0.0, 0.0].into(),
862 [0.0, 1.0].into(),
863 EuclideanTrajectory::new(),
864 );
865 assert_eq!(obstacle_space.trajectory_free(&t), false);
866
867 let t = FullTrajOwned::new(
868 [1.0, 10.0].into(),
869 [0.0, 10.0].into(),
870 EuclideanTrajectory::new(),
871 );
872 assert_eq!(obstacle_space.trajectory_free(&t), true);
873 }
874
875 #[test]
876 fn test_intersection_ball() {
877 let ball = Ball::new(0.75);
878 let ball = Obstacle2df32::no_offset(SharedShape::new(ball));
879 let t = FullTrajOwned::new(
880 [0.0, 0.0].into(),
881 [0.0, 1.0].into(),
882 EuclideanTrajectory::new(),
883 );
884 assert_eq!(ball.trajectory_free(&t), false);
885 let t = FullTrajOwned::new(
886 [1.0, 0.0].into(),
887 [0.0, 1.0].into(),
888 EuclideanTrajectory::new(),
889 );
890 assert_eq!(ball.trajectory_free(&t), false);
891 let t = FullTrajOwned::new(
892 [-1.0, 0.0].into(),
893 [1.0, 0.0].into(),
894 EuclideanTrajectory::new(),
895 );
896 assert_eq!(ball.trajectory_free(&t), false);
897 let t = FullTrajOwned::new(
898 [1.0, -1.0].into(),
899 [1.0, 1.0].into(),
900 EuclideanTrajectory::new(),
901 );
902 assert_eq!(ball.trajectory_free(&t), true);
903 let t = FullTrajOwned::new(
904 [0.7500001, -1.0].into(),
905 [0.7500001, 1.0].into(),
906 EuclideanTrajectory::new(),
907 );
908 assert_eq!(ball.trajectory_free(&t), true);
909 }
910}