path_planning/obstacles/
obstacles_3d_f64.rs

1/* Copyright (C) 2020 Dylan Staatz - All Rights Reserved. */
2
3pub use parry3d_f64 as parry;
4
5////////////////////////////////////////////////////////////////////////////////
6
7use 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 = Obstacle3df64;
33pub type OS = ObstacleSpace3df64;
34
35/// A set of a bunch of obstacles
36#[derive(Clone, Serialize, Deserialize)]
37#[serde(
38  from = "Vec<(ObstacleId, Obstacle3df64)>",
39  into = "Vec<(ObstacleId, Obstacle3df64)>"
40)]
41pub struct ObstacleSpace3df64 {
42  obstacles: HashMap<ObstacleId, Obstacle3df64>,
43}
44
45impl ObstacleSpace3df64 {
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, Obstacle3df64)>,
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<&Obstacle3df64> {
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, &Obstacle3df64)> {
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, &Obstacle3df64)> {
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    // TODO: Implement some kind of line of sight based sensing
114    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 ObstacleSpace3df64 {
131  type Params = Vec<<Obstacle3df64 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(Obstacle3df64::from_params(obs_params)?);
137    }
138
139    Ok(obs.into_iter().collect())
140  }
141}
142
143impl FromIterator<(ObstacleId, Obstacle3df64)> for ObstacleSpace3df64 {
144  fn from_iter<I>(iter: I) -> Self
145  where
146    I: IntoIterator<Item = (ObstacleId, Obstacle3df64)>,
147  {
148    Self {
149      obstacles: HashMap::from_iter(iter),
150    }
151  }
152}
153
154impl FromIterator<Obstacle3df64> for ObstacleSpace3df64 {
155  fn from_iter<I>(iter: I) -> Self
156  where
157    I: IntoIterator<Item = Obstacle3df64>,
158  {
159    Self {
160      obstacles: HashMap::from_iter(iter.into_iter().enumerate()),
161    }
162  }
163}
164
165impl IntoIterator for ObstacleSpace3df64 {
166  type Item = (ObstacleId, Obstacle3df64);
167  type IntoIter = IntoIter<ObstacleId, Obstacle3df64>;
168  fn into_iter(self) -> Self::IntoIter {
169    self.obstacles.into_iter()
170  }
171}
172
173impl FromIterator<(Isometry<Real>, SharedShape)> for ObstacleSpace3df64 {
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 ObstacleSpace3df64 {
187  fn from(static_obstacles: Vec<(Isometry<Real>, SharedShape)>) -> Self {
188    static_obstacles.into_iter().collect()
189  }
190}
191
192impl From<Vec<Obstacle3df64>> for ObstacleSpace3df64 {
193  fn from(static_obstacles: Vec<Obstacle3df64>) -> Self {
194    static_obstacles.into_iter().collect()
195  }
196}
197
198impl From<Vec<(ObstacleId, Obstacle3df64)>> for ObstacleSpace3df64 {
199  fn from(static_obstacles: Vec<(ObstacleId, Obstacle3df64)>) -> Self {
200    static_obstacles.into_iter().collect()
201  }
202}
203
204impl Into<Vec<(ObstacleId, Obstacle3df64)>> for ObstacleSpace3df64 {
205  fn into(self) -> Vec<(ObstacleId, Obstacle3df64)> {
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 ObstacleSpace3df64 {
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 ObstacleSpace3df64 {
252    type Obs = Obstacle3df64;
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 Obstacle3df64 {
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 Obstacle3df64 {
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 ObstacleSpace3df64
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 ObstacleSpace3df64
418  where
419    CS: LeaderFollowerCSpace<X, D, N>,
420  {
421    type Obs = Obstacle3df64;
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 Obstacle3df64
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 Obstacle3df64
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 3d shapes supported by parry
554/// except Compound, Polyline, or TriMesh
555#[derive(Clone, Serialize, Deserialize)]
556pub struct Obstacle3df64 {
557  shape: SharedShape,
558  offset: Isometry<X>,
559  sensed: bool,
560  aabb: AABB,
561}
562
563impl Obstacle3df64 {
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 Obstacle3df64 {
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(Obstacle3df64::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 Obstacle3df64 {
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      // Deserialization seems to fail for Compound shape
798      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      // Deserialization seems to fail for Polyline shape
815      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      // Deserialization seems to fail for TriMesh shape
851      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() -> ObstacleSpace3df64 {
863    ObstacleSpace3df64::from(get_shapes())
864  }
865
866  fn get_obstacles() -> Vec<Obstacle3df64> {
867    get_shapes().into_iter().map(Obstacle3df64::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<Obstacle3df64> = 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 = Obstacle3df64::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}