// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
// The random geometry, in the sense that matters here: the caller brings the
// random numbers and this only turns them into a point with the right
// distribution. A source that gives uniform values in `[0, 1)` is the whole
// contract, so these can be tested against fixed inputs, and no generator is
// baked into the library.
///|
/// A point uniformly distributed over the surface of the unit sphere.
///
/// `u1` and `u2` are independent uniform samples in `[0, 1)`. The height is
/// sampled uniformly rather than the polar angle, which is what keeps the
/// distribution even: sampling the angle uniformly would crowd the poles.
pub fn Vec3::random_on_sphere(u1 : Float, u2 : Float) -> Vec3 {
let z : Float = 1.0 - 2.0 * u1
let one : Float = 1.0
let radius : Float = (one - z * z).max(0.0).sqrt()
let phi : Float = tau * u2
{ x: radius * @math.cosf(phi), y: radius * @math.sinf(phi), z, }
}
///|
/// A point uniformly distributed through the unit ball.
///
/// `u1`, `u2` and `u3` are independent uniform samples in `[0, 1)`. The radius is
/// the cube root of `u3`, not `u3` itself: the volume grows as the cube of the
/// radius, so a linear radius would pile the points near the centre.
pub fn Vec3::random_in_ball(u1 : Float, u2 : Float, u3 : Float) -> Vec3 {
let direction = Vec3::random_on_sphere(u1, u2)
let third : Float = 1.0 / 3.0
let radius : Float = @math.powf(u3, third)
direction.scale(radius)
}
///|
/// A point uniformly distributed through the box.
///
/// `u1`, `u2` and `u3` are independent uniform samples in `[0, 1)`, one per axis.
pub fn Aabb3::random_point(
self : Aabb3,
u1 : Float,
u2 : Float,
u3 : Float,
) -> Vec3 {
let extent = self.size()
{
x: self.min.x + extent.x * u1,
y: self.min.y + extent.y * u2,
z: self.min.z + extent.z * u3,
}
}
///|
/// A point uniformly distributed over the surface of the sphere.
///
/// `u1` and `u2` are independent uniform samples in `[0, 1)`, as for
/// `Vec3::random_on_sphere`.
pub fn Sphere::random_on_surface(self : Sphere, u1 : Float, u2 : Float) -> Vec3 {
self.center + Vec3::random_on_sphere(u1, u2).scale(self.radius)
}
///|
/// A point uniformly distributed through the sphere.
///
/// `u1`, `u2` and `u3` are independent uniform samples in `[0, 1)`, as for
/// `Vec3::random_in_ball`.
pub fn Sphere::random_inside(
self : Sphere,
u1 : Float,
u2 : Float,
u3 : Float,
) -> Vec3 {
self.center + Vec3::random_in_ball(u1, u2, u3).scale(self.radius)
}
///|
/// A uniformly distributed random rotation.
///
/// `u1`, `u2` and `u3` are independent uniform samples in `[0, 1)`. The result is
/// a unit quaternion spread evenly over the rotation group — the construction
/// that makes it even is not obvious, and a quaternion built from three evenly
/// sampled angles would not be.
pub fn Quat::random(u1 : Float, u2 : Float, u3 : Float) -> Quat {
let one : Float = 1.0
let a : Float = (one - u1).max(0.0).sqrt()
let b : Float = u1.max(0.0).sqrt()
let phi : Float = tau * u2
let theta : Float = tau * u3
{
x: a * @math.sinf(phi),
y: a * @math.cosf(phi),
z: b * @math.sinf(theta),
w: b * @math.cosf(theta),
}
}