path_planning/obstacles/
obstacles_2d_f64.rs

1/* Copyright (C) 2020 Dylan Staatz - All Rights Reserved. */
2
3pub use parry2d_f64 as parry;
4
5////////////////////////////////////////////////////////////////////////////////
6
7use std::collections::hash_map::IntoIter;
8use std::collections::HashMap;
9use std::f64::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 = Obstacle2df64;
37pub type OS = ObstacleSpace2df64;
38
39/// A set of a bunch of obstacles
40#[derive(Clone, Serialize, Deserialize)]
41#[serde(
42  from = "Vec<(ObstacleId, Obstacle2df64)>",
43  into = "Vec<(ObstacleId, Obstacle2df64)>"
44)]
45pub struct ObstacleSpace2df64 {
46  obstacles: HashMap<ObstacleId, Obstacle2df64>,
47}
48
49impl ObstacleSpace2df64 {
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, Obstacle2df64)>,
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<&Obstacle2df64> {
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, &Obstacle2df64)> {
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, &Obstacle2df64)> {
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    // TODO: Implement some kind of line of sight based sensing
118    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 ObstacleSpace2df64 {
135  type Params = Vec<<Obstacle2df64 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(Obstacle2df64::from_params(obs_params)?);
141    }
142
143    Ok(obs.into_iter().collect())
144  }
145}
146
147impl FromIterator<(ObstacleId, Obstacle2df64)> for ObstacleSpace2df64 {
148  fn from_iter<I>(iter: I) -> Self
149  where
150    I: IntoIterator<Item = (ObstacleId, Obstacle2df64)>,
151  {
152    Self {
153      obstacles: HashMap::from_iter(iter),
154    }
155  }
156}
157
158impl FromIterator<Obstacle2df64> for ObstacleSpace2df64 {
159  fn from_iter<I>(iter: I) -> Self
160  where
161    I: IntoIterator<Item = Obstacle2df64>,
162  {
163    Self {
164      obstacles: HashMap::from_iter(iter.into_iter().enumerate()),
165    }
166  }
167}
168
169impl IntoIterator for ObstacleSpace2df64 {
170  type Item = (ObstacleId, Obstacle2df64);
171  type IntoIter = IntoIter<ObstacleId, Obstacle2df64>;
172  fn into_iter(self) -> Self::IntoIter {
173    self.obstacles.into_iter()
174  }
175}
176
177impl FromIterator<(Isometry<Real>, SharedShape)> for ObstacleSpace2df64 {
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 ObstacleSpace2df64 {
191  fn from(static_obstacles: Vec<(Isometry<Real>, SharedShape)>) -> Self {
192    static_obstacles.into_iter().collect()
193  }
194}
195
196impl From<Vec<Obstacle2df64>> for ObstacleSpace2df64 {
197  fn from(static_obstacles: Vec<Obstacle2df64>) -> Self {
198    static_obstacles.into_iter().collect()
199  }
200}
201
202impl From<Vec<(ObstacleId, Obstacle2df64)>> for ObstacleSpace2df64 {
203  fn from(static_obstacles: Vec<(ObstacleId, Obstacle2df64)>) -> Self {
204    static_obstacles.into_iter().collect()
205  }
206}
207
208impl Into<Vec<(ObstacleId, Obstacle2df64)>> for ObstacleSpace2df64 {
209  fn into(self) -> Vec<(ObstacleId, Obstacle2df64)> {
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 ObstacleSpace2df64 {
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 ObstacleSpace2df64 {
254    type Obs = Obstacle2df64;
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 Obstacle2df64 {
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 Obstacle2df64 {
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 ObstacleSpace2df64
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      // Breakout
361      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      // Segment implementation
369      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      // Breakout
398      let leader = CS::get_leader(x);
399      let follower = CS::get_follower(x);
400
401      let identity = Isometry::identity();
402
403      // Segment implementation
404      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 ObstacleSpace2df64
418  where
419    CS: LeaderFollowerCSpace<X, D, N>,
420  {
421    type Obs = Obstacle2df64;
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 Obstacle2df64
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      // Breakout
482      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      // Segment implementation
490      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      // Breakout
517      let leader = CS::get_leader(x);
518      let follower = CS::get_follower(x);
519
520      let identity = Isometry::identity();
521
522      // Segment implementation
523      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 Obstacle2df64
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/// An obstacle that can be any of the 2d shapes supported by parry
554/// except Compound, Polyline, or TriMesh
555#[derive(Clone, Serialize, Deserialize)]
556pub struct Obstacle2df64 {
557  shape: SharedShape,
558  offset: Isometry<X>,
559  sensed: bool,
560  aabb: AABB,
561}
562
563impl Obstacle2df64 {
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 Obstacle2df64 {
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(Obstacle2df64::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 Obstacle2df64 {
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      // Deserialization seems to fail for Compound shape
793      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      // Deserialization seems to fail for Polyline shape
806      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      // Deserialization seems to fail for TriMesh shape
831      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() -> ObstacleSpace2df64 {
843    ObstacleSpace2df64::from(get_shapes())
844  }
845
846  fn get_obstacles() -> Vec<Obstacle2df64> {
847    get_shapes().into_iter().map(Obstacle2df64::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<Obstacle2df64> = 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 = Obstacle2df64::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}