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}