Uniformly distributed random rotations, unit vectors

This commit is contained in:
Benjamin Saunders 2018-07-03 19:06:03 -07:00 committed by Sébastien Crozet
parent c9be27abb5
commit 352e71656d
4 changed files with 76 additions and 8 deletions

View File

@ -5,8 +5,11 @@ documented here.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [0.16.0] - WIP ## [0.16.0] - WIP
## Modified
* Adjust `UnitQuaternion`s and `Rotation3`s generated from the `Standard` distribution to be uniformly distributed.
### Added ### Added
* Add construction of a `Point` from an array by implementing the `From` trait. * Add construction of a `Point` from an array by implementing the `From` trait.
* Add support for generating uniformly distributed random unit column vectors using the `Standard` distribution.
## [0.15.0] ## [0.15.0]
The most notable change of this release is the support for using part of the library without the rust standard The most notable change of this release is the support for using part of the library without the rust standard

View File

@ -6,12 +6,12 @@ use quickcheck::{Arbitrary, Gen};
use num::{Bounded, One, Zero}; use num::{Bounded, One, Zero};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use rand; use rand;
use rand::distributions::{Distribution, Standard}; use rand::distributions::{Distribution, Standard, StandardNormal};
use rand::Rng; use rand::Rng;
use std::iter; use std::iter;
use typenum::{self, Cmp, Greater}; use typenum::{self, Cmp, Greater};
use alga::general::{ClosedAdd, ClosedMul}; use alga::general::{ClosedAdd, ClosedMul, Real};
use base::allocator::Allocator; use base::allocator::Allocator;
use base::dimension::{Dim, DimName, Dynamic, U1, U2, U3, U4, U5, U6}; use base::dimension::{Dim, DimName, Dynamic, U1, U2, U3, U4, U5, U6};
@ -501,6 +501,19 @@ where
} }
} }
#[cfg(feature = "std")]
impl<N: Real, D: DimName> Distribution<Unit<VectorN<N, D>>> for Standard
where
DefaultAllocator: Allocator<N, D>,
StandardNormal: Distribution<N>,
{
#[inline]
fn sample<'a, G: Rng + ?Sized>(&self, rng: &'a mut G) -> Unit<VectorN<N, D>> {
Unit::new_normalize(VectorN::from_distribution_generic(D::name(), U1, &mut StandardNormal, rng))
}
}
/* /*
* *
* Constructors for small matrices and vectors. * Constructors for small matrices and vectors.

View File

@ -6,7 +6,7 @@ use base::storage::Owned;
use quickcheck::{Arbitrary, Gen}; use quickcheck::{Arbitrary, Gen};
use num::{One, Zero}; use num::{One, Zero};
use rand::distributions::{Distribution, Standard}; use rand::distributions::{Distribution, Standard, OpenClosed01};
use rand::Rng; use rand::Rng;
use alga::general::Real; use alga::general::Real;
@ -413,11 +413,25 @@ impl<N: Real> One for UnitQuaternion<N> {
impl<N: Real> Distribution<UnitQuaternion<N>> for Standard impl<N: Real> Distribution<UnitQuaternion<N>> for Standard
where where
Standard: Distribution<N>, OpenClosed01: Distribution<N>,
{ {
#[inline] #[inline]
fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> UnitQuaternion<N> { fn sample<'a, R: Rng + ?Sized>(&self, rng: &'a mut R) -> UnitQuaternion<N> {
UnitQuaternion::from_scaled_axis(rng.gen::<Vector3<N>>() * N::two_pi()) // Ken Shoemake's Subgroup Algorithm
// Uniform random rotations.
// In D. Kirk, editor, Graphics Gems III, pages 124-132. Academic, New York, 1992.
let x0 = rng.sample(OpenClosed01);
let x1 = rng.sample(OpenClosed01);
let x2 = rng.sample(OpenClosed01);
let theta1 = N::two_pi() * x1;
let theta2 = N::two_pi() * x2;
let s1 = theta1.sin();
let c1 = theta1.cos();
let s2 = theta2.sin();
let c2 = theta2.cos();
let r1 = (N::one() - x0).sqrt();
let r2 = x0.sqrt();
Unit::new_unchecked(Quaternion::new(s1 * r1, c1 * r1, s2 * r2, c2 * r2))
} }
} }
@ -433,3 +447,18 @@ where
UnitQuaternion::from_scaled_axis(axisangle) UnitQuaternion::from_scaled_axis(axisangle)
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use rand::{self, SeedableRng};
#[test]
fn random_unit_quats_are_unit() {
let mut rng = rand::prng::XorShiftRng::from_seed([0xAB; 16]);
for _ in 0..1000 {
let x = rng.gen::<UnitQuaternion<f32>>();
assert!(relative_eq!(x.unwrap().norm(), 1.0))
}
}
}

View File

@ -5,7 +5,7 @@ use quickcheck::{Arbitrary, Gen};
use alga::general::Real; use alga::general::Real;
use num::Zero; use num::Zero;
use rand::distributions::{Distribution, Standard}; use rand::distributions::{Distribution, Standard, OpenClosed01};
use rand::Rng; use rand::Rng;
use std::ops::Neg; use std::ops::Neg;
@ -384,11 +384,34 @@ impl<N: Real> Rotation3<N> {
impl<N: Real> Distribution<Rotation3<N>> for Standard impl<N: Real> Distribution<Rotation3<N>> for Standard
where where
Standard: Distribution<N>, OpenClosed01: Distribution<N>,
{ {
#[inline] #[inline]
fn sample<'a, R: Rng + ?Sized>(&self, rng: &mut R) -> Rotation3<N> { fn sample<'a, R: Rng + ?Sized>(&self, rng: &mut R) -> Rotation3<N> {
Rotation3::new(rng.gen::<Vector3<N>>() * N::two_pi()) // James Arvo.
// Fast random rotation matrices.
// In D. Kirk, editor, Graphics Gems III, pages 117-120. Academic, New York, 1992.
// Compute a random rotation around Z
let theta = N::two_pi() * rng.sample(OpenClosed01);
let (ts, tc) = theta.sin_cos();
let a = MatrixN::<N, U3>::new(
tc, ts, N::zero(),
-ts, tc, N::zero(),
N::zero(), N::zero(), N::one()
);
// Compute a random rotation *of* Z
let phi = N::two_pi() * rng.sample(OpenClosed01);
let z = rng.sample(OpenClosed01);
let (ps, pc) = phi.sin_cos();
let sqrt_z = z.sqrt();
let v = Vector3::new(pc * sqrt_z, ps * sqrt_z, (N::one() - z).sqrt());
let mut b = v * v.transpose();
b += b;
b -= MatrixN::<N, U3>::identity();
Rotation3::from_matrix_unchecked(b * a)
} }
} }