Skip to content

Commit

Permalink
Add feature "random" for random color generation using rand crate
Browse files Browse the repository at this point in the history
Implement Distribution for Standard from rand for all color types, LabHue, and RgbHue
Implement SampleUniform and UniformSampler for all color types, LabHue, and RgbHue

Color sampling is supported for `gen`, `gen_range`, and `Uniform`.
Equations for cone sampling are placed in their own module and include
unit tests.
Hwb is implemented in terms of Hsv and wraps around the Hsv sampler and
implementation.

Co-authored-by: Erik Hedvall <erikwhedvall@gmail.com>
  • Loading branch information
okaneco and Ogeon committed Apr 1, 2020
1 parent acecaee commit 27178c6
Show file tree
Hide file tree
Showing 15 changed files with 1,228 additions and 26 deletions.
6 changes: 6 additions & 0 deletions palette/Cargo.toml
Expand Up @@ -18,6 +18,7 @@ build = "build/main.rs"
default = ["named_from_str", "std"]
named_from_str = ["named", "phf", "phf_codegen", "std"]
named = []
random = ["rand"]
serializing = ["serde", "std"]

#ignore in feature test
Expand All @@ -33,6 +34,11 @@ approx = {version = "0.3", default-features = false}
version = "0.8"
optional = true

[dependencies.rand]
version = "0.7"
default-features = false
optional = true

[dependencies.serde]
version = "1"
features = ["serde_derive"]
Expand Down
83 changes: 83 additions & 0 deletions palette/src/alpha.rs
Expand Up @@ -2,6 +2,12 @@ use core::fmt;
use core::ops::{Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};

use approx::{AbsDiffEq, RelativeEq, UlpsEq};
#[cfg(feature = "random")]
use rand::distributions::uniform::{SampleBorrow, SampleUniform, Uniform, UniformSampler};
#[cfg(feature = "random")]
use rand::distributions::{Distribution, Standard};
#[cfg(feature = "random")]
use rand::Rng;

use crate::blend::PreAlpha;
use crate::encoding::pixel::RawPixel;
Expand Down Expand Up @@ -435,6 +441,83 @@ where
}
}

#[cfg(feature = "random")]
impl<C, T> Distribution<Alpha<C, T>> for Standard
where
T: Component,
Standard: Distribution<C> + Distribution<T>,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Alpha<C, T> {
Alpha {
color: rng.gen(),
alpha: rng.gen(),
}
}
}

#[cfg(feature = "random")]
pub struct UniformAlpha<C, T>
where
T: Component + SampleUniform,
C: SampleUniform,
{
color: Uniform<C>,
alpha: Uniform<T>,
}

#[cfg(feature = "random")]
impl<C, T> SampleUniform for Alpha<C, T>
where
T: Component + SampleUniform,
C: Copy + SampleUniform,
{
type Sampler = UniformAlpha<C, T>;
}

#[cfg(feature = "random")]
impl<C, T> UniformSampler for UniformAlpha<C, T>
where
T: Component + SampleUniform,
C: Copy + SampleUniform,
{
type X = Alpha<C, T>;

fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();

UniformAlpha {
color: Uniform::new::<C, _>(low.color, high.color),
alpha: Uniform::new::<_, T>(low.alpha, high.alpha),
}
}

fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();

UniformAlpha {
color: Uniform::new_inclusive::<C, _>(low.color, high.color),
alpha: Uniform::new_inclusive::<_, T>(low.alpha, high.alpha),
}
}

fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Alpha<C, T> {
Alpha {
color: self.color.sample(rng),
alpha: self.alpha.sample(rng),
}
}
}

#[cfg(test)]
mod test {
use crate::encoding::Srgb;
Expand Down
98 changes: 98 additions & 0 deletions palette/src/hsl.rs
Expand Up @@ -3,6 +3,12 @@ use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub, SubAssign};

use approx::{AbsDiffEq, RelativeEq, UlpsEq};
#[cfg(feature = "random")]
use rand::distributions::uniform::{SampleBorrow, SampleUniform, Uniform, UniformSampler};
#[cfg(feature = "random")]
use rand::distributions::{Distribution, Standard};
#[cfg(feature = "random")]
use rand::Rng;

use crate::encoding::pixel::RawPixel;
use crate::encoding::{Linear, Srgb};
Expand Down Expand Up @@ -657,6 +663,98 @@ where
}
}

#[cfg(feature = "random")]
impl<S, T> Distribution<Hsl<S, T>> for Standard
where
T: FloatComponent,
S: RgbSpace,
Standard: Distribution<T>,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Hsl<S, T> {
crate::random_sampling::sample_hsl(rng.gen::<RgbHue<T>>(), rng.gen(), rng.gen())
}
}

#[cfg(feature = "random")]
pub struct UniformHsl<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
hue: crate::hues::UniformRgbHue<T>,
u1: Uniform<T>,
u2: Uniform<T>,
space: PhantomData<S>,
}

#[cfg(feature = "random")]
impl<S, T> SampleUniform for Hsl<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
type Sampler = UniformHsl<S, T>;
}

#[cfg(feature = "random")]
impl<S, T> UniformSampler for UniformHsl<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
type X = Hsl<S, T>;

fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
use crate::random_sampling::invert_hsl_sample;

let low = *low_b.borrow();
let high = *high_b.borrow();

let (r1_min, r2_min) = invert_hsl_sample(low);
let (r1_max, r2_max) = invert_hsl_sample(high);

UniformHsl {
hue: crate::hues::UniformRgbHue::new(low.hue, high.hue),
u1: Uniform::new::<_, T>(r1_min, r1_max),
u2: Uniform::new::<_, T>(r2_min, r2_max),
space: PhantomData,
}
}

fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
use crate::random_sampling::invert_hsl_sample;

let low = *low_b.borrow();
let high = *high_b.borrow();

let (r1_min, r2_min) = invert_hsl_sample(low);
let (r1_max, r2_max) = invert_hsl_sample(high);

UniformHsl {
hue: crate::hues::UniformRgbHue::new_inclusive(low.hue, high.hue),
u1: Uniform::new_inclusive::<_, T>(r1_min, r1_max),
u2: Uniform::new_inclusive::<_, T>(r2_min, r2_max),
space: PhantomData,
}
}

fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Hsl<S, T> {
crate::random_sampling::sample_hsl(
self.hue.sample(rng),
self.u1.sample(rng),
self.u2.sample(rng),
)
}
}

#[cfg(test)]
mod test {
use super::Hsl;
Expand Down
112 changes: 108 additions & 4 deletions palette/src/hsv.rs
Expand Up @@ -3,16 +3,20 @@ use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub, SubAssign};

use approx::{AbsDiffEq, RelativeEq, UlpsEq};
#[cfg(feature = "random")]
use rand::distributions::uniform::{SampleBorrow, SampleUniform, Uniform, UniformSampler};
#[cfg(feature = "random")]
use rand::distributions::{Distribution, Standard};
#[cfg(feature = "random")]
use rand::Rng;

use crate::encoding::pixel::RawPixel;
use crate::encoding::{Linear, Srgb};
use crate::float::Float;
use crate::rgb::{Rgb, RgbSpace};
use crate::{clamp, contrast_ratio, from_f64};
use crate::{Alpha, Hsl, Hwb, Xyz};
use crate::{
Component, FloatComponent, FromColor, FromF64, GetHue, Hue, IntoColor, Limited, Mix, Pixel,
RelativeContrast, RgbHue, Saturate, Shade,
clamp, contrast_ratio, from_f64, Alpha, Component, FloatComponent, FromColor, FromF64, GetHue,
Hsl, Hue, Hwb, IntoColor, Limited, Mix, Pixel, RelativeContrast, RgbHue, Saturate, Shade, Xyz,
};

/// Linear HSV with an alpha component. See the [`Hsva` implementation in
Expand Down Expand Up @@ -672,6 +676,106 @@ where
}
}

#[cfg(feature = "random")]
impl<S, T> Distribution<Hsv<S, T>> for Standard
where
T: FloatComponent,
S: RgbSpace,
Standard: Distribution<T>,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Hsv<S, T> {
crate::random_sampling::sample_hsv(rng.gen::<RgbHue<T>>(), rng.gen(), rng.gen())
}
}

#[cfg(feature = "random")]
pub struct UniformHsv<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
hue: crate::hues::UniformRgbHue<T>,
u1: Uniform<T>,
u2: Uniform<T>,
space: PhantomData<S>,
}

#[cfg(feature = "random")]
impl<S, T> SampleUniform for Hsv<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
type Sampler = UniformHsv<S, T>;
}

#[cfg(feature = "random")]
impl<S, T> UniformSampler for UniformHsv<S, T>
where
T: FloatComponent + SampleUniform,
S: RgbSpace + SampleUniform,
{
type X = Hsv<S, T>;

fn new<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();

let (r1_min, r2_min) = (
low.value * low.value * low.value,
low.saturation * low.saturation,
);
let (r1_max, r2_max) = (
high.value * high.value * high.value,
high.saturation * high.saturation,
);

UniformHsv {
hue: crate::hues::UniformRgbHue::new(low.hue, high.hue),
u1: Uniform::new::<_, T>(r1_min, r1_max),
u2: Uniform::new::<_, T>(r2_min, r2_max),
space: PhantomData,
}
}

fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Self
where
B1: SampleBorrow<Self::X> + Sized,
B2: SampleBorrow<Self::X> + Sized,
{
let low = *low_b.borrow();
let high = *high_b.borrow();

let (r1_min, r2_min) = (
low.value * low.value * low.value,
low.saturation * low.saturation,
);
let (r1_max, r2_max) = (
high.value * high.value * high.value,
high.saturation * high.saturation,
);

UniformHsv {
hue: crate::hues::UniformRgbHue::new_inclusive(low.hue, high.hue),
u1: Uniform::new_inclusive::<_, T>(r1_min, r1_max),
u2: Uniform::new_inclusive::<_, T>(r2_min, r2_max),
space: PhantomData,
}
}

fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Hsv<S, T> {
crate::random_sampling::sample_hsv(
self.hue.sample(rng),
self.u1.sample(rng),
self.u2.sample(rng),
)
}
}

#[cfg(test)]
mod test {
use super::Hsv;
Expand Down

0 comments on commit 27178c6

Please sign in to comment.