Skip to main content

wassily_noise/
lib.rs

1//! # Wassily Noise
2//!
3//! Noise generation utilities optimized for generative art applications.
4//! This crate provides ergonomic wrappers around the Rust noise library with
5//! f32 support, additional noise functions, and utilities specifically designed
6//! for creative coding and generative art.
7//!
8//! ## Key Features
9//!
10//! - **f32 Support**: All noise functions work with f32 instead of f64 for better performance
11//! - **Scale-Independent**: Noise scaling that adapts to canvas size automatically
12//! - **Generative Art Focus**: Additional noise types specifically useful for creative coding
13//! - **Easy Integration**: Seamless integration with wassily's canvas and shape systems
14//!
15//! ## Available Noise Types
16//!
17//! - **Core Functions**: [`noise2d()`], [`noise3d()`] with normalized variants
18//! - **[`curl`]**: Curl noise for fluid-like effects and vector fields
19//! - **[`gabor`]**: Gabor noise for procedural textures and patterns
20//! - **[`white`]**: White noise and pseudo-random number generation
21//! - **[`sinusoid`]**: Sinusoidal noise patterns
22//! - **[`img_noise`]**: Image-based noise generation
23//!
24//! ## Quick Start
25//!
26//! ```no_run
27//! use wassily_noise::*;
28//! use noise::{NoiseFn, Perlin};
29//!
30//! // Create a noise function
31//! let perlin = Perlin::new(42);
32//! let opts = NoiseOpts::default().scales(0.01);
33//!
34//! // Generate noise values
35//! let value = noise2d(perlin, &opts, 100.0, 150.0);      // Range: [-1, 1]
36//! let normalized = noise2d_01(perlin, &opts, 100.0, 150.0); // Range: [0, 1]
37//! ```
38//!
39//! ## Noise Options
40//!
41//! The [`NoiseOpts`] struct provides fine-grained control over noise generation:
42//!
43//! ```no_run
44//! use wassily_noise::*;
45//!
46//! let opts = NoiseOpts::default()
47//!     .width(800.0)
48//!     .height(600.0)
49//!     .scales(0.005)        // Uniform scaling
50//!     .factor(2.0);         // Amplitude multiplier
51//! ```
52use noise::NoiseFn;
53use num_traits::{AsPrimitive, ToPrimitive};
54
55pub mod curl;
56pub mod gabor;
57pub mod img_noise;
58pub mod sinusoid;
59pub mod white;
60
61// Re-export key types and functions for convenience
62pub use curl::*;
63pub use gabor::*;
64pub use img_noise::*;
65pub use sinusoid::*;
66pub use white::*;
67
68/// Configuration options for noise generation.
69///
70/// `NoiseOpts` provides fine-grained control over how noise coordinates are scaled
71/// and how the output is modified. This allows for canvas-size-independent noise
72/// generation and consistent results across different output resolutions.
73///
74/// ## Scaling Behavior
75///
76/// - **Input Scaling**: Coordinates are scaled by `scale / (width or height)`
77/// - **Output Scaling**: Results are multiplied by `factor`
78/// - **Canvas Independence**: Noise patterns remain consistent regardless of canvas size
79///
80/// ## Example
81///
82/// ```no_run
83/// use wassily_noise::*;
84///
85/// // Create noise options for a 800x600 canvas
86/// let opts = NoiseOpts::default()
87///     .width(800.0)
88///     .height(600.0)
89///     .scales(0.01)     // Fine detail
90///     .factor(2.0);     // Amplify output
91///
92/// // Same pattern will look identical on any canvas size
93/// let opts_large = NoiseOpts::default()
94///     .width(1600.0)
95///     .height(1200.0)
96///     .scales(0.01)
97///     .factor(2.0);
98/// ```
99#[derive(Debug, Clone, Copy)]
100pub struct NoiseOpts {
101    pub width: f32,
102    pub height: f32,
103    pub x_scale: f32,
104    pub y_scale: f32,
105    pub z_scale: f32,
106    pub factor: f32,
107}
108
109impl NoiseOpts {
110    pub fn new(
111        width: f32,
112        height: f32,
113        x_scale: f32,
114        y_scale: f32,
115        z_scale: f32,
116        factor: f32,
117    ) -> Self {
118        Self {
119            width,
120            height,
121            x_scale,
122            y_scale,
123            z_scale,
124            factor,
125        }
126    }
127
128    pub fn with_wh<T: AsPrimitive<f32>>(width: T, height: T) -> Self {
129        Self {
130            width: width.as_(),
131            height: height.as_(),
132            ..Self::default()
133        }
134    }
135
136    pub fn width(self, width: f32) -> Self {
137        Self { width, ..self }
138    }
139
140    pub fn height(self, height: f32) -> Self {
141        Self { height, ..self }
142    }
143
144    /// Multiplier for the noise value.
145    pub fn factor(self, factor: f32) -> Self {
146        Self { factor, ..self }
147    }
148
149    /// Used to scale the x-coordinate: `x = x_scale * x / width`.
150    pub fn x_scale(self, x_scale: f32) -> Self {
151        Self { x_scale, ..self }
152    }
153
154    /// Used to scale the y-coordinate: `y = y_scale * y / height`.
155    pub fn y_scale(self, y_scale: f32) -> Self {
156        Self { y_scale, ..self }
157    }
158
159    /// Used to scale the z-coordinate: `z = z_scale * z`.
160    pub fn z_scale(self, z_scale: f32) -> Self {
161        Self { z_scale, ..self }
162    }
163
164    /// Set both the x and y scales to the same value.
165    pub fn xy_scales(self, scale: f32) -> Self {
166        Self {
167            x_scale: scale,
168            y_scale: scale,
169            ..self
170        }
171    }
172
173    /// Set all scales to the same value.
174    pub fn scales(self, scale: f32) -> Self {
175        Self {
176            x_scale: scale,
177            y_scale: scale,
178            z_scale: scale,
179            ..self
180        }
181    }
182}
183
184impl Default for NoiseOpts {
185    fn default() -> Self {
186        Self {
187            width: 1.0,
188            height: 1.0,
189            x_scale: 1.0,
190            y_scale: 1.0,
191            z_scale: 1.0,
192            factor: 1.0,
193        }
194    }
195}
196
197fn get_f32<const N: usize>(nf: impl NoiseFn<f64, N>, point: [f32; N]) -> f32 {
198    let coords = point.iter().map(|p| p.to_f64());
199    let mut a: [f64; N] = [0.0; N];
200    for (i, c) in coords.enumerate() {
201        a[i] = c.unwrap();
202    }
203    nf.get(a) as f32
204}
205
206/// Get a f32 noise value in the range [-1.0, 1.0].
207pub fn noise2d(nf: impl NoiseFn<f64, 2>, opts: &NoiseOpts, x: f32, y: f32) -> f32 {
208    opts.factor
209        * get_f32(
210            nf,
211            [
212                1.0 / opts.width * opts.x_scale * x,
213                1.0 / opts.height * opts.y_scale * y,
214            ],
215        )
216}
217
218/// Get a f32 noise value in the range [0.0, 1.0].
219pub fn noise2d_01(nf: impl NoiseFn<f64, 2>, opts: &NoiseOpts, x: f32, y: f32) -> f32 {
220    0.5 * noise2d(&nf, opts, x, y) + 0.5
221}
222
223/// Get a f32 noise value in the range [-1.0, 1.0].
224pub fn noise3d(nf: impl NoiseFn<f64, 3>, opts: &NoiseOpts, x: f32, y: f32, z: f32) -> f32 {
225    opts.factor
226        * get_f32(
227            nf,
228            [
229                1.0 / opts.width * opts.x_scale * x,
230                1.0 / opts.height * opts.y_scale * y,
231                opts.z_scale * z,
232            ],
233        )
234}
235
236/// Get a f32 noise value in the range [0.0, 1.0].
237pub fn noise3d_01(nf: impl NoiseFn<f64, 3>, opts: &NoiseOpts, x: f32, y: f32, z: f32) -> f32 {
238    0.5 * noise3d(&nf, opts, x, y, z) + 0.5
239}