Crates.io | attract |
lib.rs | attract |
version | 0.0.0 |
created_at | 2025-05-27 15:55:46.754578+00 |
updated_at | 2025-05-27 15:55:46.754578+00 |
description | A small library for visualsing dynamical systems, particularly strange attractors. |
homepage | |
repository | https://github.com/FreddyWordingham/attract |
max_upload_size | |
id | 1691283 |
size | 8,198,678 |
Generate beautiful mathematical art with the power of chaos theory and Rust's performance.
f32
, f64
)Add this to your Cargo.toml
:
[dependencies]
attract = "0.0.0"
use attract::{Clifford, Generator, Settings, render};
use nalgebra::Complex;
// Create a Clifford attractor with classic parameters
let attractor = Box::new(Clifford::new(-1.4, 1.6, 1.3, 0.7));
// Set up a Gaussian generator for initial sampling points
let generator = Generator::Gaussian {
centre: Complex::new(0.0, 0.0),
std_dev: 1.0,
};
// Configure rendering settings
let settings = Settings {
attractor,
generator: &generator,
resolution: [1024, 1024],
offset: [0.0, 0.0],
scale: 5.0,
num_samples: 1_000_000,
num_groups: 100,
max_iter: 10_000,
warmup: 1_000,
};
// Render the attractor
let density_map = render(&settings);
// The density_map is a 2D array where each cell contains
// the number of points that visited that pixel
One of the most visually striking attractors, generating intricate butterfly-like patterns.
Equations:
x_{n+1} = sin(a * y_n) + c * cos(a * x_n)
y_{n+1} = sin(b * x_n) + d * cos(b * y_n)
let clifford = Clifford::new(-1.4, 1.6, 1.3, 0.7);
Creates organic, flowing patterns with four parameters controlling the dynamics.
Equations:
x_{n+1} = sin(a * y_n) - cos(b * x_n)
y_{n+1} = sin(c * x_n) - cos(d * y_n)
let de_jong = DeJong::new(-2.0, -2.0, -1.2, 2.0);
A classic discrete-time dynamical system that exhibits chaotic behavior.
Equations:
x_{n+1} = 1 - a * x_n^2 + y_n
y_{n+1} = b * x_n
let henon = Henon::new(1.4, 0.3);
Models the behavior of light in an optical cavity, creating spiral patterns.
Equations:
t = 0.4 - 6.0 / (1 + x_n^2 + y_n^2)
x_{n+1} = 1 + u * (x_n * cos(t) - y_n * sin(t))
y_{n+1} = u * (x_n * sin(t) + y_n * cos(t))
let ikeda = Ikeda::new(0.918);
A four-parameter map that produces fairy-like patterns, often used in fractal art.
Equations:
x_{n+1} = x_n^2 - y_n^2 + a * x_n + b * y_n
y_{n+1} = 2 * x_n * y_n + c * x_n + d * y_n
let tinkerbell = Tinkerbell::new(0.9, -0.6013, 2.0, 0.5);
A chaotic oscillator model that simulates the behavior of a forced, damped oscillator.
Equations:
x_{n+1} = y_n
y_{n+1} = -b * x_n + a * y_n - y_n^3
let duffing = Duffing::new(-1.0, 1.0);
A unique attractor with a distinctive shape, defined by a simple iterative process.
Equations:
x_{n+1} = 1 - y_n + |x_n|
y_{n+1} = x_n
let gingerbreadman = Gingerbreadman::new();
A map that exhibits chaotic behavior in Hamiltonian systems, often used in studies of chaos.
Equations:
impl<T: Float + Copy> Attractor
let p_new = p_momentum + self.k * x.sin();
let x_new = x + p_new;
Complex::new(x_new, p_new)
}
}
x_{n+1} = x_n + p_n
p_{n+1} = p_n + k * sin(x_n)
The library is designed to be extensible. You can implement your own custom attractors by simply implementing the Attractor
trait:
use attract::Attractor;
use nalgebra::Complex;
use num_traits::Float;
// Example: A simple custom attractor
#[derive(Debug, Clone, Copy)]
pub struct MyCustomAttractor<T> {
param_a: T,
param_b: T,
}
impl<T> MyCustomAttractor<T> {
pub const fn new(a: T, b: T) -> Self {
Self { param_a: a, param_b: b }
}
}
impl<T: Float + Copy> Attractor<T> for MyCustomAttractor<T> {
fn iterate(&self, p: Complex<T>) -> Complex<T> {
// Define your custom iteration equations here
let x = p.re;
let y = p.im;
Complex::new(
// Your x_{n+1} equation
self.param_a * x + self.param_b * y.sin(),
// Your y_{n+1} equation
y * x - self.param_a
)
}
}
// Your custom attractor now works with all rendering functions!
let custom_attractor = Box::new(MyCustomAttractor::new(1.2, 0.8));
let settings = Settings {
attractor: custom_attractor,
// ... rest of your settings
};
let result = render(&settings);
Once you implement the Attractor
trait, your custom attractor is immediately compatible with all the library's rendering functions, sampling methods, and performance optimizations.
Control how initial points are distributed in the complex plane:
let generator = Generator::Gaussian {
centre: Complex::new(0.0, 0.0),
std_dev: 1.0,
};
let generator = Generator::Circle {
centre: Complex::new(0.0, 0.0),
radius: 2.0,
};
let generator = Generator::Aabb {
centre: Complex::new(0.0, 0.0),
half_size: Complex::new(1.0, 1.0),
};
Control parallelization with the num_groups
parameter:
let settings = Settings {
// ... other settings
num_groups: 100, // Adjust based on your CPU cores
// ...
};
num_samples
: Better quality, more computation timemax_iter
: More detailed attractors, slower renderingresolution
: Higher detail, more memory usagewarmup
iterations: Skip transient behavior, focus on the attractorFor quick previews:
num_samples: 100_000,
max_iter: 1_000,
resolution: [512, 512],
For high-quality renders:
num_samples: 10_000_000,
max_iter: 50_000,
resolution: [2048, 2048],
Focus on specific regions of the attractor:
let settings = Settings {
// ... other settings
offset: [1.2, -0.8], // Center the view here
scale: 2.0, // Zoom level (smaller = more zoomed in)
// ...
};
Use different precision levels:
// High precision
let attractor = Box::new(Clifford::<f64>::new(-1.4, 1.6, 1.3, 0.7));
// Fast computation
let attractor = Box::new(Clifford::<f32>::new(-1.4, 1.6, 1.3, 0.7));
The render
function returns a 2D density array (Array2<u32>
) where each cell contains the visit count for that pixel. You can:
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request. Areas where contributions would be particularly valuable: