1use nalgebra::constraint::{SameNumberOfRows, ShapeConstraint};
4use nalgebra::geometry::UnitQuaternion;
5use nalgebra::storage::Storage;
6use nalgebra::{Const, Dim, RealField, SVector, Unit, Vector};
7use num_traits::Float;
8use rand::distributions::{uniform::SampleUniform, Distribution};
9use rand::{thread_rng, SeedableRng};
10use serde::{de::DeserializeOwned, Deserialize, Serialize};
11
12use crate::error::{InvalidParamError, Result};
13use crate::params::FromParams;
14use crate::rng::{LinearCoordinates, RNG};
15use crate::scalar::Scalar;
16use crate::trajectories::{EuclideanTrajectory, FullTraj};
17use crate::util::bounds::Bounds;
18use crate::util::math::{atan2, unit_d_ball_vol};
19
20use super::super::CSpace;
21use super::polar::saturate_polar_zero;
22use super::LeaderFollowerCSpace;
23
24pub const D: usize = 3;
25pub const N: usize = D * 2;
26
27#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
28#[serde(bound(
29 serialize = "X: Serialize",
30 deserialize = "X: DeserializeOwned"
31))]
32pub struct LeaderFollowerSphericalSpaceParams<X: Scalar> {
33 pub bounds: Bounds<X, D>,
34 pub seed: Option<u64>,
35 pub sensor_range: (X, X),
36}
37
38pub struct LeaderFollowerSphericalSpace<X>
46where
47 X: Scalar + SampleUniform,
48{
49 bounds: Bounds<X, D>,
50 volume: X,
51 rng: RNG,
52 distribution: LinearCoordinates<X, N>,
53 intial_sensor_range: (X, X),
54 sensor_range: (X, X),
55 sensor_range_cubed: (X, X),
56}
57
58impl<X> LeaderFollowerSphericalSpace<X>
59where
60 X: Scalar + SampleUniform,
61{
62 pub fn new(
63 bounds: Bounds<X, D>,
64 rng: RNG,
65 sensor_range: (X, X),
66 ) -> Result<Self> {
67 debug_assert_eq!(D * 2, N);
68
69 if !bounds.is_valid() {
71 Err(InvalidParamError {
72 parameter_name: "bounds",
73 parameter_value: format!("{:?}", bounds),
74 })?;
75 }
76
77 if !(sensor_range.0 < sensor_range.1) {
79 Err(InvalidParamError {
80 parameter_name: "sensor_range",
81 parameter_value: format!("{:?}", sensor_range),
82 })?;
83 }
84
85 let sensor_range_cubed = (
86 sensor_range.0 * sensor_range.0 * sensor_range.0,
87 sensor_range.1 * sensor_range.1 * sensor_range.1,
88 );
89
90 let sensor_space_volume = (sensor_range_cubed.1 * unit_d_ball_vol(D))
91 - (sensor_range_cubed.0 * unit_d_ball_vol(D));
92 let volume = bounds.volume() * sensor_space_volume;
93
94 let mins = SVector::<X, N>::from([
95 bounds.mins[0], bounds.mins[1], bounds.mins[2], X::zero(), X::zero(), -X::frac_pi_2(), ]);
102
103 let maxs = SVector::<X, N>::from([
104 bounds.maxs[0], bounds.maxs[1], bounds.maxs[2], X::one(), X::two_pi(), X::frac_pi_2(), ]);
111
112 let distribution = LinearCoordinates::new(mins, maxs);
113
114 Ok(Self {
115 bounds,
116 volume,
117 rng,
118 distribution,
119 intial_sensor_range: sensor_range,
120 sensor_range,
121 sensor_range_cubed,
122 })
123 }
124
125 pub fn intial_sensor_range(&self) -> (X, X) {
126 self.intial_sensor_range
127 }
128
129 pub fn get_sensor_range(&self) -> (X, X) {
130 self.sensor_range
131 }
132
133 pub fn set_sensor_range(&mut self, sensor_range: (X, X)) -> Option<()> {
134 if sensor_range.0 < sensor_range.1 {
135 self.sensor_range = sensor_range;
137 self.sensor_range_cubed = (
138 sensor_range.0 * sensor_range.0 * sensor_range.0,
139 sensor_range.1 * sensor_range.1 * sensor_range.1,
140 );
141 Some(())
142 } else {
143 None
145 }
146 }
147}
148
149impl<X> LeaderFollowerCSpace<X, D, N> for LeaderFollowerSphericalSpace<X>
150where
151 X: Scalar + SampleUniform,
152{
153 }
155
156impl<X> CSpace<X, N> for LeaderFollowerSphericalSpace<X>
157where
158 X: Scalar + SampleUniform,
159{
160 type Traj = EuclideanTrajectory<X, N>;
161
162 fn volume(&self) -> X {
163 self.volume
164 }
165
166 fn cost<R1, R2, S1, S2>(
167 &self,
168 a: &Vector<X, R1, S1>,
169 b: &Vector<X, R2, S2>,
170 ) -> X
171 where
172 X: Scalar,
173 R1: Dim,
174 R2: Dim,
175 S1: Storage<X, R1>,
176 S2: Storage<X, R2>,
177 ShapeConstraint: SameNumberOfRows<R1, R2>
178 + SameNumberOfRows<R1, Const<N>>
179 + SameNumberOfRows<R2, Const<N>>,
180 {
181 a.metric_distance(b)
182 }
183
184 fn trajectory<S1, S2>(
185 &self,
186 start: Vector<X, Const<N>, S1>,
187 end: Vector<X, Const<N>, S2>,
188 ) -> Option<FullTraj<X, Self::Traj, S1, S2, N>>
189 where
190 X: Scalar,
191 S1: Storage<X, Const<N>>,
192 S2: Storage<X, Const<N>>,
193 {
194 Some(FullTraj::new(start, end, EuclideanTrajectory::new()))
195 }
196
197 fn is_free<S>(&self, a: &Vector<X, Const<N>, S>) -> bool
198 where
199 S: Storage<X, Const<N>>,
200 {
201 let leader_abs = a.fixed_rows::<D>(0);
202 let follower_abs = a.fixed_rows::<D>(D);
203
204 if !self.bounds.within(&follower_abs) {
205 return false;
206 }
207
208 let rho2 = leader_abs.metric_distance(&follower_abs);
210 self.sensor_range.0 <= rho2 && rho2 <= self.sensor_range.1
211 }
212
213 fn saturate(&self, a: &mut SVector<X, N>, b: &SVector<X, N>, delta: X) {
214 let delta = delta / (X::one() + X::one());
215
216 let mut a_lead_mut = a.fixed_rows_mut::<D>(0);
218 let b_lead = b.fixed_rows::<D>(0);
219
220 let lead_scale = delta / a_lead_mut.metric_distance(&b_lead);
221
222 a_lead_mut.set_column(0, &(&a_lead_mut - &b_lead));
223 a_lead_mut.set_column(0, &(&a_lead_mut * lead_scale));
224 a_lead_mut.set_column(0, &(&a_lead_mut + &b_lead));
225
226 let a_lead = a.fixed_rows::<D>(0);
228 let a_fol = a.fixed_rows::<D>(D);
229
230 let mut b = b.clone(); let mut b_lead_mut = b.fixed_rows_mut::<D>(0);
233 b_lead_mut.set_column(0, &a_lead); let mut b_rel = abs_to_rel(&b); let mut b_fol_rel_mut = b_rel.fixed_rows_mut::<D>(D).into_owned();
238 let a_fol_rel = cartesian_to_spherical(&a_fol);
239
240 let mut delta = delta;
242 if self.sensor_range.1 < b_fol_rel_mut[0] {
243 delta -= b_fol_rel_mut[0] - self.sensor_range.1;
244 b_fol_rel_mut[0] = self.sensor_range.1;
245 }
246 if b_fol_rel_mut[0] < self.sensor_range.0 {
247 delta -= self.sensor_range.0 - b_fol_rel_mut[0];
248 b_fol_rel_mut[0] = self.sensor_range.0;
249 }
250
251 if X::zero() <= delta {
252 let mut star_fol_rel =
253 saturate_spherical(&a_fol_rel, &b_fol_rel_mut, delta);
254
255 log::debug!(
256 "sensor_range: {:?}, star_fol_rel: {:?}",
257 self.sensor_range,
258 <[X; D]>::from(star_fol_rel)
259 );
260
261 if self.sensor_range.1 < star_fol_rel[0] {
263 star_fol_rel[0] = self.sensor_range.1;
264 }
265 if star_fol_rel[0] < self.sensor_range.0 {
266 star_fol_rel[0] = self.sensor_range.0;
267 }
268
269 let mut a_fol_mut = a.fixed_rows_mut::<D>(D);
271 a_fol_mut.set_column(0, &star_fol_rel);
272 } else {
273 let mut a_fol_mut = a.fixed_rows_mut::<D>(D);
275 a_fol_mut.set_column(0, &b_fol_rel_mut);
276 }
277
278 *a = rel_to_abs(&a);
280 }
281
282 fn sample(&mut self) -> SVector<X, N> {
283 let mut sample = self.distribution.sample(&mut self.rng);
284
285 let a_3 = self.sensor_range_cubed.0;
287 let b_3 = self.sensor_range_cubed.1;
288 sample[3] = <X as Float>::cbrt(sample[3] * (b_3 - a_3) + a_3);
289
290 rel_to_abs(&sample)
291 }
292}
293
294impl<X> FromParams for LeaderFollowerSphericalSpace<X>
295where
296 X: Scalar + SampleUniform,
297{
298 type Params = LeaderFollowerSphericalSpaceParams<X>;
299 fn from_params(params: Self::Params) -> Result<Self> {
300 let rng = match params.seed {
301 Some(seed) => RNG::seed_from_u64(seed),
302 None => RNG::from_rng(thread_rng())?,
303 };
304
305 LeaderFollowerSphericalSpace::new(params.bounds, rng, params.sensor_range)
306 }
307}
308
309pub fn rel_to_abs<X, R, S>(v: &Vector<X, R, S>) -> SVector<X, N>
313where
314 X: RealField + Copy,
315 R: Dim,
316 S: Storage<X, R>,
317 ShapeConstraint: SameNumberOfRows<R, Const<N>>,
318{
319 let x1 = v[0];
320 let y1 = v[1];
321 let z1 = v[2];
322 let x2 = x1 + (v[3] * v[5].cos() * v[4].cos());
323 let y2 = y1 + (v[3] * v[5].cos() * v[4].sin());
324 let z2 = z1 + (v[3] * v[5].sin());
325
326 [x1, y1, z1, x2, y2, z2].into()
327}
328
329pub fn spherical_to_cartesian<X, R, S>(v: &Vector<X, R, S>) -> SVector<X, D>
331where
332 X: RealField + Copy,
333 R: Dim,
334 S: Storage<X, R>,
335 ShapeConstraint: SameNumberOfRows<R, Const<D>>,
336{
337 [
338 v[0] * v[2].cos() * v[1].cos(),
339 v[0] * v[2].cos() * v[1].sin(),
340 v[0] * v[2].sin(),
341 ]
342 .into()
343}
344
345pub fn abs_to_rel<X, R, S>(v: &Vector<X, R, S>) -> SVector<X, N>
349where
350 X: RealField + Copy,
351 R: Dim,
352 S: Storage<X, R>,
353 ShapeConstraint: SameNumberOfRows<R, Const<N>>,
354{
355 let x1 = v[0];
356 let y1 = v[1];
357 let z1 = v[2];
358 let x2 = v[3] - x1; let y2 = v[4] - y1; let z2 = v[5] - z1; let rho2 = (x2 * x2 + y2 * y2 + z2 * z2).sqrt();
363 let lambda2 = atan2(y2, x2).unwrap_or(X::zero());
364 let phi2 = atan2(z2, (x2 * x2 + y2 * y2).sqrt()).unwrap_or(X::zero());
365
366 [x1, y1, z1, rho2, bound_lambda(lambda2), bound_phi(phi2)].into()
367}
368
369pub fn cartesian_to_spherical<X, R, S>(v: &Vector<X, R, S>) -> SVector<X, D>
371where
372 X: RealField + Copy,
373 R: Dim,
374 S: Storage<X, R>,
375 ShapeConstraint: SameNumberOfRows<R, Const<D>>,
376{
377 let rho2 = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
378 let lambda2 = atan2(v[1], v[0]).unwrap_or(X::zero());
379 let phi2 =
380 atan2(v[2], (v[0] * v[0] + v[1] * v[1]).sqrt()).unwrap_or(X::zero());
381 [rho2, bound_lambda(lambda2), bound_phi(phi2)].into()
382}
383
384fn saturate_spherical<X, R1, S1, R2, S2>(
385 a: &Vector<X, R1, S1>,
386 b: &Vector<X, R2, S2>,
387 delta: X,
388) -> SVector<X, D>
389where
390 X: RealField + Copy,
391 R1: Dim,
392 R2: Dim,
393 S1: Storage<X, R1>,
394 S2: Storage<X, R2>,
395 ShapeConstraint: SameNumberOfRows<R1, R2>
396 + SameNumberOfRows<R1, Const<D>>
397 + SameNumberOfRows<R2, Const<D>>,
398{
399 let rho_a = a[0];
400 let lambda_a = a[1];
401 let phi_a = a[2];
402 let rho_b = b[0];
403 let lambda_b = b[1];
404 let phi_b = b[2];
405
406 let n_a = SVector::<X, D>::from([
408 phi_a.cos() * lambda_a.cos(),
409 phi_a.cos() * lambda_a.sin(),
410 phi_a.sin(),
411 ]);
412
413 let n_b = SVector::<X, D>::from([
414 phi_b.cos() * lambda_b.cos(),
415 phi_b.cos() * lambda_b.sin(),
416 phi_b.sin(),
417 ]);
418
419 let n = n_b.cross(&n_a);
421 let (n, magnitude) = Unit::new_and_get(n);
422
423 let omega = atan2(magnitude, n_b.dot(&n_a)).unwrap_or(X::zero());
425
426 let r_a = rho_a;
428 let theta_a = omega;
429 let r_b = rho_b;
430
431 log::debug!("a: [{:?}, {:?}]", r_a, theta_a);
432 log::debug!("b: [{:?}, {:?}]", r_b, 0.0);
433
434 let (r_star, theta_star) = saturate_polar_zero(r_a, theta_a, r_b, delta);
435
436 log::debug!("*: [{:?}, {:?}]", r_star, theta_star);
437
438 let rotation = UnitQuaternion::from_axis_angle(&n, theta_star);
440 let v = rotation * n_b;
441
442 let rho_star = r_star;
444 let lambda_star = atan2(v.y, v.x).unwrap_or(X::zero());
445 let phi_star =
446 atan2(v.z, (v.x.powi(2) + v.y.powi(2)).sqrt()).unwrap_or(X::zero());
447
448 [rho_star, bound_lambda(lambda_star), bound_phi(phi_star)].into()
449}
450
451fn bound_lambda<X>(mut lambda: X) -> X
453where
454 X: RealField + Copy,
455{
456 while lambda < X::zero() {
458 lambda += X::two_pi()
459 }
460 while X::two_pi() <= lambda {
462 lambda -= X::two_pi()
463 }
464 lambda
465}
466
467fn bound_phi<X>(mut phi: X) -> X
469where
470 X: RealField + Copy,
471{
472 while phi < -X::frac_pi_2() {
474 phi += X::pi()
475 }
476 while X::frac_pi_2() < phi {
478 phi -= X::pi()
479 }
480 phi
481}