Skip to main content

mgart/util/
mod.rs

1use serde::{Deserialize, Serialize};
2
3use num::complex::Complex64;
4
5use log::{log_enabled, Level};
6
7use num::cast;
8
9use std::sync::atomic::{AtomicU64, Ordering};
10
11pub mod coloring;
12pub mod frame;
13pub mod gradient;
14pub mod post_processing;
15pub mod sampler;
16pub mod viewport;
17
18/// Representation of a complex number.
19///
20/// This is intended to be used as means for parsing user input,
21/// not for doing calculations.
22/// So [`ComplexNumber`] does not implement any math operations,
23/// but supports the conversion to [Complex64].
24///
25#[derive(Serialize, Deserialize, Clone, Copy)]
26#[serde(untagged)]
27pub enum ComplexNumber {
28  Cartesian { re: f64, im: f64 },
29  Polar { r: f64, theta: f64 },
30}
31
32impl From<&ComplexNumber> for Complex64 {
33  fn from(cn: &ComplexNumber) -> Self {
34    match cn {
35      ComplexNumber::Cartesian { re, im } => Self::new(*re, *im),
36      ComplexNumber::Polar { r, theta } => {
37        Self::from_polar(*r, *theta)
38      }
39    }
40  }
41}
42
43impl From<ComplexNumber> for Complex64 {
44  fn from(cn: ComplexNumber) -> Self {
45    Self::from(&cn)
46  }
47}
48
49// TODO: dz is derivative of z in the iteration sequence ... if I were
50//       to look for finite attractors for different functions, I'd
51//       need to generalize this
52#[must_use]
53pub fn finite_attractor(
54  z0: Complex64,
55  c: Complex64,
56  p: usize,
57) -> Option<(Complex64, Complex64)> {
58  let mut zz = z0;
59
60  for _ in 0..64 {
61    let mut z = zz;
62    let mut dz = Complex64::new(1., 0.);
63
64    for _ in 0..p {
65      dz = 2. * z * dz;
66      z = z.powi(2) + c;
67    }
68
69    let zz_new = zz - (z - zz) / (dz - 1.);
70
71    if (zz_new - zz).norm_sqr() <= 1e-20 {
72      return Some((z, dz));
73    }
74
75    zz = zz_new;
76  }
77
78  None
79}
80
81/// Thread-safe progress printer for printing the number of iterations
82/// done by a computationally expensive loop.
83///
84pub enum ProgressPrinter {
85  Info(InfoProgressPrinter),
86  Disabled,
87}
88
89impl ProgressPrinter {
90  #[must_use]
91  pub fn new(n: u64, interval: u64) -> Self {
92    if log_enabled!(Level::Info) {
93      Self::Info(InfoProgressPrinter::new(n, interval))
94    } else {
95      Self::Disabled
96    }
97  }
98
99  pub fn increment(&self) {
100    if let Self::Info(info) = self {
101      info.increment();
102    }
103  }
104}
105
106pub struct InfoProgressPrinter {
107  counter: AtomicU64,
108  n: u64,
109  n_f64: f64,
110  interval: u64,
111}
112
113impl InfoProgressPrinter {
114  /// Creates a new instance of [`InfoProgressPrinter`].
115  ///
116  /// # Panics
117  ///
118  /// Panics, if [`n`] overflows the 53 bit mantissa of [f64].
119  ///
120  #[must_use]
121  pub fn new(n: u64, interval: u64) -> Self {
122    Self {
123      counter: AtomicU64::new(0),
124      n,
125      n_f64: cast::<_, f64>(n).unwrap(),
126      interval,
127    }
128  }
129
130  /// Increments the counter, printing a status update if the counter
131  /// equals a multiple of [`interval`] or [`n`].
132  ///
133  /// # Panics
134  ///
135  /// Panics, if the counter overflows the 53 bit mantissa of [f64].
136  ///
137  pub fn increment(&self) {
138    let i = self.counter.fetch_add(1, Ordering::SeqCst);
139
140    if i % self.interval == self.interval - 1 || i == self.n - 1 {
141      let p = cast::<_, f64>(i).unwrap() / self.n_f64 * 100.;
142      print!("{}/{} iterations done ({:.2}%)\r", i + 1, self.n, p);
143    }
144  }
145}