1use rand::RngExt;
6use std::collections::HashMap;
7
8pub struct Lacunarity;
13impl Lacunarity {
14 pub fn gliding_box(grid: &[Vec<bool>], box_size: usize) -> f64 {
21 let rows = grid.len();
22 if rows == 0 || box_size == 0 {
23 return 0.0;
24 }
25 let cols = grid[0].len();
26 if box_size > rows || box_size > cols {
27 return 0.0;
28 }
29 let mut masses = Vec::new();
30 for r in 0..=(rows - box_size) {
31 for c in 0..=(cols - box_size) {
32 let mut mass = 0u64;
33 for dr in 0..box_size {
34 for dc in 0..box_size {
35 if grid[r + dr][c + dc] {
36 mass += 1;
37 }
38 }
39 }
40 masses.push(mass as f64);
41 }
42 }
43 if masses.is_empty() {
44 return 0.0;
45 }
46 let n = masses.len() as f64;
47 let mean = masses.iter().sum::<f64>() / n;
48 if mean.abs() < 1e-15 {
49 return 0.0;
50 }
51 let variance = masses.iter().map(|&m| (m - mean).powi(2)).sum::<f64>() / n;
52 variance / (mean * mean) + 1.0
53 }
54 pub fn from_points(points: &[Point2], resolution: usize, box_size: usize) -> f64 {
58 if points.is_empty() || resolution == 0 {
59 return 0.0;
60 }
61 let (min_x, max_x, min_y, max_y) = FractalDimension::bounding_box(points);
62 let dx = (max_x - min_x).max(1e-15);
63 let dy = (max_y - min_y).max(1e-15);
64 let mut grid = vec![vec![false; resolution]; resolution];
65 for p in points {
66 let ix = (((p.x - min_x) / dx) * (resolution - 1) as f64)
67 .round()
68 .clamp(0.0, (resolution - 1) as f64) as usize;
69 let iy = (((p.y - min_y) / dy) * (resolution - 1) as f64)
70 .round()
71 .clamp(0.0, (resolution - 1) as f64) as usize;
72 grid[iy][ix] = true;
73 }
74 Self::gliding_box(&grid, box_size)
75 }
76 pub fn multiscale(grid: &[Vec<bool>], box_sizes: &[usize]) -> Vec<(usize, f64)> {
80 box_sizes
81 .iter()
82 .map(|&bs| (bs, Self::gliding_box(grid, bs)))
83 .collect()
84 }
85}
86pub struct SierpinskiArrowhead;
88impl SierpinskiArrowhead {
89 pub fn lsystem() -> LSystem {
91 LSystem::new(
92 vec!['F', 'G', '+', '-'],
93 vec![
94 ProductionRule::new('F', "G-F-G"),
95 ProductionRule::new('G', "F+G+F"),
96 ],
97 "F",
98 )
99 }
100 pub fn theoretical_dimension() -> f64 {
102 (3.0_f64).ln() / (2.0_f64).ln()
103 }
104 pub fn generate(generations: usize, step_size: f64) -> Vec<(Point2, Point2)> {
106 let ls = Self::lsystem();
107 ls.turtle_interpret(generations, step_size, 60.0)
108 }
109}
110#[derive(Debug, Clone)]
112pub struct IteratedFunctionSystem {
113 pub transforms: Vec<AffineTransform2D>,
115 pub probabilities: Vec<f64>,
117}
118impl IteratedFunctionSystem {
119 pub fn new(transforms: Vec<AffineTransform2D>, probabilities: Vec<f64>) -> Self {
123 if transforms.is_empty() {
124 return Self {
125 transforms: Vec::new(),
126 probabilities: Vec::new(),
127 };
128 }
129 assert_eq!(
130 transforms.len(),
131 probabilities.len(),
132 "transforms and probabilities must have equal length"
133 );
134 Self {
135 transforms,
136 probabilities,
137 }
138 }
139 pub fn with_equal_probabilities(transforms: Vec<AffineTransform2D>) -> Self {
141 let n = transforms.len();
142 assert!(n > 0, "IFS must have at least one transform");
143 let prob = 1.0 / n as f64;
144 let probabilities = vec![prob; n];
145 Self {
146 transforms,
147 probabilities,
148 }
149 }
150 pub fn chaos_game(&self, start: Point2, iterations: usize, skip: usize) -> Vec<Point2> {
156 use rand::RngExt;
157 let mut rng = rand::rng();
158 let mut point = start;
159 let mut points = Vec::with_capacity(iterations.saturating_sub(skip));
160 let cumulative = self.cumulative_distribution();
161 for i in 0..iterations {
162 let r: f64 = rng.random_range(0.0..1.0);
163 let idx = cumulative
164 .iter()
165 .position(|&c| r < c)
166 .unwrap_or(self.transforms.len() - 1);
167 point = self.transforms[idx].apply(&point);
168 if i >= skip {
169 points.push(point);
170 }
171 }
172 points
173 }
174 pub fn deterministic_iterate(
179 &self,
180 initial_points: &[Point2],
181 generations: usize,
182 ) -> Vec<Point2> {
183 let mut current = initial_points.to_vec();
184 for _ in 0..generations {
185 let mut next = Vec::with_capacity(current.len() * self.transforms.len());
186 for t in &self.transforms {
187 for p in ¤t {
188 next.push(t.apply(p));
189 }
190 }
191 current = next;
192 }
193 current
194 }
195 pub fn num_transforms(&self) -> usize {
197 self.transforms.len()
198 }
199 pub fn barnsley_fern() -> Self {
201 let transforms = vec![
202 AffineTransform2D::new(0.0, 0.0, 0.0, 0.16, 0.0, 0.0),
203 AffineTransform2D::new(0.85, 0.04, -0.04, 0.85, 0.0, 1.6),
204 AffineTransform2D::new(0.20, -0.26, 0.23, 0.22, 0.0, 1.6),
205 AffineTransform2D::new(-0.15, 0.28, 0.26, 0.24, 0.0, 0.44),
206 ];
207 let probabilities = vec![0.01, 0.85, 0.07, 0.07];
208 Self::new(transforms, probabilities)
209 }
210 pub fn generate(&self, n: usize, seed: u64) -> Vec<[f64; 2]> {
214 if self.transforms.is_empty() {
215 return Vec::new();
216 }
217 let cumulative = self.cumulative_distribution();
218 let mut x = 0.0_f64;
219 let mut y = 0.0_f64;
220 let mut rng_state = seed;
221 let mut points = Vec::with_capacity(n);
222 for _ in 0..n {
224 rng_state = rng_state
225 .wrapping_mul(6364136223846793005)
226 .wrapping_add(1442695040888963407);
227 let r = ((rng_state >> 33) as f64) / (u32::MAX as f64);
228 let idx = cumulative
229 .iter()
230 .position(|&c| r < c)
231 .unwrap_or(self.transforms.len() - 1);
232 let t = &self.transforms[idx];
233 let nx = t.a * x + t.b * y + t.e;
234 let ny = t.c * x + t.d * y + t.f;
235 x = nx;
236 y = ny;
237 points.push([x, y]);
238 }
239 points
240 }
241 fn cumulative_distribution(&self) -> Vec<f64> {
243 let mut cum = Vec::with_capacity(self.probabilities.len());
244 let mut acc = 0.0;
245 for &p in &self.probabilities {
246 acc += p;
247 cum.push(acc);
248 }
249 cum
250 }
251}
252#[derive(Debug, Clone)]
254pub struct ProductionRule {
255 pub predecessor: char,
257 pub successor: String,
259}
260impl ProductionRule {
261 pub fn new(predecessor: char, successor: impl Into<String>) -> Self {
263 Self {
264 predecessor,
265 successor: successor.into(),
266 }
267 }
268}
269pub struct MandelbrotSet {
274 pub max_iter: u32,
276 pub escape_radius_sq: f64,
278}
279impl MandelbrotSet {
280 pub fn new(max_iter: u32, escape_radius: f64) -> Self {
282 Self {
283 max_iter,
284 escape_radius_sq: escape_radius * escape_radius,
285 }
286 }
287 pub fn default_set() -> Self {
289 Self::new(256, 2.0)
290 }
291 pub fn escape_time(&self, cr: f64, ci: f64) -> Option<u32> {
296 let mut zr = 0.0;
297 let mut zi = 0.0;
298 for i in 0..self.max_iter {
299 let zr2 = zr * zr;
300 let zi2 = zi * zi;
301 if zr2 + zi2 > self.escape_radius_sq {
302 return Some(i);
303 }
304 zi = 2.0 * zr * zi + ci;
305 zr = zr2 - zi2 + cr;
306 }
307 None
308 }
309 pub fn smooth_escape_time(&self, cr: f64, ci: f64) -> f64 {
313 let mut zr = 0.0;
314 let mut zi = 0.0;
315 for i in 0..self.max_iter {
316 let zr2 = zr * zr;
317 let zi2 = zi * zi;
318 if zr2 + zi2 > self.escape_radius_sq {
319 let modulus = (zr2 + zi2).sqrt();
320 let mu = i as f64 - modulus.ln().ln() / (2.0_f64).ln();
321 return mu;
322 }
323 zi = 2.0 * zr * zi + ci;
324 zr = zr2 - zi2 + cr;
325 }
326 self.max_iter as f64
327 }
328 pub fn is_in_set(&self, cr: f64, ci: f64) -> bool {
330 self.escape_time(cr, ci).is_none()
331 }
332 pub fn is_boundary(&self, cr: f64, ci: f64, delta: f64) -> bool {
337 let center = self.is_in_set(cr, ci);
338 let offsets = [(delta, 0.0), (-delta, 0.0), (0.0, delta), (0.0, -delta)];
339 for (dr, di) in offsets {
340 if self.is_in_set(cr + dr, ci + di) != center {
341 return true;
342 }
343 }
344 false
345 }
346 pub fn area_monte_carlo(
352 &self,
353 x_min: f64,
354 x_max: f64,
355 y_min: f64,
356 y_max: f64,
357 num_samples: usize,
358 ) -> f64 {
359 let mut rng = rand::rng();
360 let mut count = 0usize;
361 for _ in 0..num_samples {
362 let cr = rng.random_range(x_min..x_max);
363 let ci = rng.random_range(y_min..y_max);
364 if self.is_in_set(cr, ci) {
365 count += 1;
366 }
367 }
368 let rect_area = (x_max - x_min) * (y_max - y_min);
369 rect_area * count as f64 / num_samples as f64
370 }
371 pub fn render_grid(
375 &self,
376 x_min: f64,
377 x_max: f64,
378 y_min: f64,
379 y_max: f64,
380 width: usize,
381 height: usize,
382 ) -> Vec<Vec<Option<u32>>> {
383 let dx = (x_max - x_min) / width as f64;
384 let dy = (y_max - y_min) / height as f64;
385 (0..height)
386 .map(|j| {
387 let ci = y_min + (j as f64 + 0.5) * dy;
388 (0..width)
389 .map(|i| {
390 let cr = x_min + (i as f64 + 0.5) * dx;
391 self.escape_time(cr, ci)
392 })
393 .collect()
394 })
395 .collect()
396 }
397}
398pub struct HilbertCurveMap;
403impl HilbertCurveMap {
404 pub fn d2xy(n: u32, d: u32) -> (u32, u32) {
406 let mut rx: u32;
407 let mut ry: u32;
408 let mut x = 0_u32;
409 let mut y = 0_u32;
410 let mut d = d;
411 let mut s = 1_u32;
412 while s < n {
413 rx = if (d & 2) != 0 { 1 } else { 0 };
414 ry = if (d & 1) != 0 {
415 if rx == 0 { 1 } else { 0 }
416 } else {
417 0
418 };
419 Self::rot(s, &mut x, &mut y, rx, ry);
420 x += s * rx;
421 y += s * ry;
422 d /= 4;
423 s *= 2;
424 }
425 (x, y)
426 }
427 pub fn xy2d(n: u32, x: u32, y: u32) -> u32 {
429 let mut rx: u32;
430 let mut ry: u32;
431 let mut d = 0_u32;
432 let mut x = x;
433 let mut y = y;
434 let mut s = n / 2;
435 while s > 0 {
436 rx = if (x & s) > 0 { 1 } else { 0 };
437 ry = if (y & s) > 0 { 1 } else { 0 };
438 d += s * s * ((3 * rx) ^ ry);
439 Self::rot(s, &mut x, &mut y, rx, ry);
440 s /= 2;
441 }
442 d
443 }
444 fn rot(n: u32, x: &mut u32, y: &mut u32, rx: u32, ry: u32) {
446 if ry == 0 {
447 if rx == 1 {
448 *x = n.wrapping_sub(1).wrapping_sub(*x);
449 *y = n.wrapping_sub(1).wrapping_sub(*y);
450 }
451 std::mem::swap(x, y);
452 }
453 }
454 pub fn generate_path(n: u32) -> Vec<(u32, u32)> {
458 let total = n * n;
459 (0..total).map(|d| Self::d2xy(n, d)).collect()
460 }
461}
462pub struct FractalTerrain;
466impl FractalTerrain {
467 pub fn diamond_square(power: u32, roughness: f64, seed_corners: [f64; 4]) -> Vec<Vec<f64>> {
473 let size = (1 << power) + 1;
474 let mut grid = vec![vec![0.0_f64; size]; size];
475 let last = size - 1;
476 grid[0][0] = seed_corners[0];
477 grid[0][last] = seed_corners[1];
478 grid[last][0] = seed_corners[2];
479 grid[last][last] = seed_corners[3];
480 let mut rng = rand::rng();
481 let mut step = last;
482 let mut scale = 1.0_f64;
483 while step > 1 {
484 let half = step / 2;
485 let mut y = 0;
486 while y < last {
487 let mut x = 0;
488 while x < last {
489 let avg = (grid[y][x]
490 + grid[y][x + step]
491 + grid[y + step][x]
492 + grid[y + step][x + step])
493 / 4.0;
494 grid[y + half][x + half] = avg + rng.random_range(-scale..scale);
495 x += step;
496 }
497 y += step;
498 }
499 let mut y = 0;
500 while y <= last {
501 let x_start = if (y / half).is_multiple_of(2) {
502 half
503 } else {
504 0
505 };
506 let mut x = x_start;
507 while x <= last {
508 let mut sum = 0.0;
509 let mut count = 0.0;
510 if y >= half {
511 sum += grid[y - half][x];
512 count += 1.0;
513 }
514 if y + half <= last {
515 sum += grid[y + half][x];
516 count += 1.0;
517 }
518 if x >= half {
519 sum += grid[y][x - half];
520 count += 1.0;
521 }
522 if x + half <= last {
523 sum += grid[y][x + half];
524 count += 1.0;
525 }
526 grid[y][x] = sum / count + rng.random_range(-scale..scale);
527 x += step;
528 }
529 y += half;
530 }
531 step = half;
532 scale *= roughness;
533 }
534 grid
535 }
536 pub fn midpoint_displacement_1d(
541 power: u32,
542 roughness: f64,
543 h_left: f64,
544 h_right: f64,
545 ) -> Vec<f64> {
546 let size = (1 << power) + 1;
547 let mut heights = vec![0.0_f64; size];
548 heights[0] = h_left;
549 heights[size - 1] = h_right;
550 let mut rng = rand::rng();
551 let mut step = size - 1;
552 let mut scale = 1.0_f64;
553 while step > 1 {
554 let half = step / 2;
555 let mut i = half;
556 while i < size {
557 let left = heights[i - half];
558 let right = if i + half < size {
559 heights[i + half]
560 } else {
561 heights[size - 1]
562 };
563 heights[i] = (left + right) / 2.0 + rng.random_range(-scale..scale);
564 i += step;
565 }
566 step = half;
567 scale *= roughness;
568 }
569 heights
570 }
571 pub fn rms_roughness(grid: &[Vec<f64>]) -> f64 {
573 let mut sum = 0.0;
574 let mut count = 0usize;
575 let mut mean = 0.0;
576 for row in grid {
577 for &h in row {
578 mean += h;
579 count += 1;
580 }
581 }
582 if count == 0 {
583 return 0.0;
584 }
585 mean /= count as f64;
586 for row in grid {
587 for &h in row {
588 sum += (h - mean).powi(2);
589 }
590 }
591 (sum / count as f64).sqrt()
592 }
593 pub fn height_range(grid: &[Vec<f64>]) -> (f64, f64) {
595 let mut lo = f64::MAX;
596 let mut hi = f64::MIN;
597 for row in grid {
598 for &h in row {
599 if h < lo {
600 lo = h;
601 }
602 if h > hi {
603 hi = h;
604 }
605 }
606 }
607 (lo, hi)
608 }
609}
610pub struct FractalMesh;
615impl FractalMesh {
616 pub fn from_heightmap(grid: &[Vec<f64>], spacing: f64) -> (Vec<[f64; 3]>, Vec<[usize; 3]>) {
621 let rows = grid.len();
622 if rows == 0 {
623 return (Vec::new(), Vec::new());
624 }
625 let cols = grid[0].len();
626 let mut vertices = Vec::with_capacity(rows * cols);
627 for (r, row) in grid.iter().enumerate() {
628 for (c, &h) in row.iter().enumerate() {
629 vertices.push([c as f64 * spacing, h, r as f64 * spacing]);
630 }
631 }
632 let mut triangles = Vec::new();
633 for r in 0..(rows - 1) {
634 for c in 0..(cols - 1) {
635 let i0 = r * cols + c;
636 let i1 = i0 + 1;
637 let i2 = i0 + cols;
638 let i3 = i2 + 1;
639 triangles.push([i0, i2, i1]);
640 triangles.push([i1, i2, i3]);
641 }
642 }
643 (vertices, triangles)
644 }
645 pub fn surface_area(vertices: &[[f64; 3]], triangles: &[[usize; 3]]) -> f64 {
647 let mut area = 0.0;
648 for tri in triangles {
649 let a = vertices[tri[0]];
650 let b = vertices[tri[1]];
651 let c = vertices[tri[2]];
652 let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
653 let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
654 let cross = [
655 ab[1] * ac[2] - ab[2] * ac[1],
656 ab[2] * ac[0] - ab[0] * ac[2],
657 ab[0] * ac[1] - ab[1] * ac[0],
658 ];
659 area += (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]).sqrt() / 2.0;
660 }
661 area
662 }
663 pub fn expected_counts(rows: usize, cols: usize) -> (usize, usize) {
665 let verts = rows * cols;
666 let tris = if rows > 1 && cols > 1 {
667 2 * (rows - 1) * (cols - 1)
668 } else {
669 0
670 };
671 (verts, tris)
672 }
673}
674pub struct HilbertCurve;
676impl HilbertCurve {
677 pub fn lsystem() -> LSystem {
679 LSystem::new(
680 vec!['A', 'B', 'F', '+', '-'],
681 vec![
682 ProductionRule::new('A', "+BF-AFA-FB+"),
683 ProductionRule::new('B', "-AF+BFB+FA-"),
684 ],
685 "A",
686 )
687 }
688 pub fn generate(generations: usize, step_size: f64) -> Vec<(Point2, Point2)> {
690 let ls = Self::lsystem();
691 ls.turtle_interpret(generations, step_size, 90.0)
692 }
693}
694pub struct FractalDimension;
696impl FractalDimension {
697 pub fn box_counting(points: &[Point2], num_scales: usize) -> (f64, f64) {
703 if points.is_empty() || num_scales < 2 {
704 return (0.0, 0.0);
705 }
706 let (min_x, max_x, min_y, max_y) = Self::bounding_box(points);
707 let extent = (max_x - min_x).max(max_y - min_y).max(1e-15);
708 let mut log_eps = Vec::with_capacity(num_scales);
709 let mut log_n = Vec::with_capacity(num_scales);
710 for i in 0..num_scales {
711 let epsilon = extent / (2.0_f64).powi(i as i32 + 1);
712 if epsilon < 1e-15 {
713 break;
714 }
715 let mut occupied = std::collections::HashSet::new();
716 for p in points {
717 let ix = ((p.x - min_x) / epsilon).floor() as i64;
718 let iy = ((p.y - min_y) / epsilon).floor() as i64;
719 occupied.insert((ix, iy));
720 }
721 let count = occupied.len() as f64;
722 if count > 0.0 {
723 log_eps.push(epsilon.ln());
724 log_n.push(count.ln());
725 }
726 }
727 if log_eps.len() < 2 {
728 return (0.0, 0.0);
729 }
730 let (slope, r_sq) = Self::linear_regression(&log_eps, &log_n);
731 (-slope, r_sq)
732 }
733 pub fn correlation_dimension(points: &[Point2], num_scales: usize) -> (f64, f64) {
738 let n = points.len();
739 if n < 10 || num_scales < 2 {
740 return (0.0, 0.0);
741 }
742 let mut max_dist = 0.0_f64;
743 let mut min_dist = f64::MAX;
744 let num_pairs = n * (n - 1) / 2;
745 let mut distances = Vec::with_capacity(num_pairs);
746 for i in 0..n {
747 for j in (i + 1)..n {
748 let d = points[i].distance(&points[j]);
749 distances.push(d);
750 if d > max_dist {
751 max_dist = d;
752 }
753 if d > 0.0 && d < min_dist {
754 min_dist = d;
755 }
756 }
757 }
758 if max_dist < 1e-15 {
759 return (0.0, 0.0);
760 }
761 distances.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
762 let mut log_r = Vec::with_capacity(num_scales);
763 let mut log_c = Vec::with_capacity(num_scales);
764 let r_min = min_dist.max(max_dist * 0.001);
765 let r_max = max_dist * 0.5;
766 let ratio = (r_max / r_min).powf(1.0 / (num_scales as f64 - 1.0));
767 for i in 0..num_scales {
768 let r = r_min * ratio.powi(i as i32);
769 let count = distances.partition_point(|&d| d <= r);
770 if count > 0 {
771 let c = count as f64 / num_pairs as f64;
772 log_r.push(r.ln());
773 log_c.push(c.ln());
774 }
775 }
776 if log_r.len() < 2 {
777 return (0.0, 0.0);
778 }
779 let (slope, r_sq) = Self::linear_regression(&log_r, &log_c);
780 (slope, r_sq)
781 }
782 pub fn hausdorff_estimate(points: &[Point2], num_scales: usize) -> f64 {
786 if points.is_empty() || num_scales < 4 {
787 return 0.0;
788 }
789 let (dim1, _) = Self::box_counting(points, num_scales);
790 let half = num_scales / 2;
791 let (dim2, _) = Self::box_counting(points, half);
792 0.6 * dim1 + 0.4 * dim2
793 }
794 pub(super) fn bounding_box(points: &[Point2]) -> (f64, f64, f64, f64) {
796 let mut min_x = f64::MAX;
797 let mut max_x = f64::MIN;
798 let mut min_y = f64::MAX;
799 let mut max_y = f64::MIN;
800 for p in points {
801 if p.x < min_x {
802 min_x = p.x;
803 }
804 if p.x > max_x {
805 max_x = p.x;
806 }
807 if p.y < min_y {
808 min_y = p.y;
809 }
810 if p.y > max_y {
811 max_y = p.y;
812 }
813 }
814 (min_x, max_x, min_y, max_y)
815 }
816 fn linear_regression(x: &[f64], y: &[f64]) -> (f64, f64) {
818 let n = x.len() as f64;
819 let sum_x: f64 = x.iter().sum();
820 let sum_y: f64 = y.iter().sum();
821 let sum_xy: f64 = x.iter().zip(y.iter()).map(|(xi, yi)| xi * yi).sum();
822 let sum_x2: f64 = x.iter().map(|xi| xi * xi).sum();
823 let sum_y2: f64 = y.iter().map(|yi| yi * yi).sum();
824 let denom = n * sum_x2 - sum_x * sum_x;
825 if denom.abs() < 1e-30 {
826 return (0.0, 0.0);
827 }
828 let slope = (n * sum_xy - sum_x * sum_y) / denom;
829 let ss_tot = sum_y2 - sum_y * sum_y / n;
830 let ss_res = sum_y2 - slope * sum_xy - (sum_y - slope * sum_x) * sum_y / n;
831 let r_sq = if ss_tot.abs() > 1e-30 {
832 1.0 - ss_res / ss_tot
833 } else {
834 0.0
835 };
836 (slope, r_sq.clamp(0.0, 1.0))
837 }
838}
839pub struct SierpinskiTriangle;
844impl SierpinskiTriangle {
845 pub fn ifs() -> IteratedFunctionSystem {
847 let transforms = vec![
848 AffineTransform2D::new(0.5, 0.0, 0.0, 0.5, 0.0, 0.0),
849 AffineTransform2D::new(0.5, 0.0, 0.0, 0.5, 0.5, 0.0),
850 AffineTransform2D::new(0.5, 0.0, 0.0, 0.5, 0.25, 0.433),
851 ];
852 IteratedFunctionSystem::with_equal_probabilities(transforms)
853 }
854 pub fn theoretical_dimension() -> f64 {
856 (3.0_f64).ln() / (2.0_f64).ln()
857 }
858 pub fn generate(num_points: usize) -> Vec<Point2> {
860 let ifs = Self::ifs();
861 ifs.chaos_game(Point2::new(0.0, 0.0), num_points + 100, 100)
862 }
863}
864#[derive(Debug, Clone, Copy, PartialEq)]
868pub struct AffineTransform2D {
869 pub a: f64,
871 pub b: f64,
873 pub c: f64,
875 pub d: f64,
877 pub e: f64,
879 pub f: f64,
881}
882impl AffineTransform2D {
883 pub fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) -> Self {
885 Self { a, b, c, d, e, f }
886 }
887 pub fn identity() -> Self {
889 Self::new(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
890 }
891 pub fn rotation(angle_rad: f64) -> Self {
893 let cos = angle_rad.cos();
894 let sin = angle_rad.sin();
895 Self::new(cos, -sin, sin, cos, 0.0, 0.0)
896 }
897 pub fn scaling(sx: f64, sy: f64) -> Self {
899 Self::new(sx, 0.0, 0.0, sy, 0.0, 0.0)
900 }
901 pub fn translation(tx: f64, ty: f64) -> Self {
903 Self::new(1.0, 0.0, 0.0, 1.0, tx, ty)
904 }
905 pub fn apply(&self, p: &Point2) -> Point2 {
907 Point2 {
908 x: self.a * p.x + self.b * p.y + self.e,
909 y: self.c * p.x + self.d * p.y + self.f,
910 }
911 }
912 pub fn compose(&self, other: &AffineTransform2D) -> AffineTransform2D {
916 AffineTransform2D {
917 a: other.a * self.a + other.b * self.c,
918 b: other.a * self.b + other.b * self.d,
919 c: other.c * self.a + other.d * self.c,
920 d: other.c * self.b + other.d * self.d,
921 e: other.a * self.e + other.b * self.f + other.e,
922 f: other.c * self.e + other.d * self.f + other.f,
923 }
924 }
925 pub fn determinant(&self) -> f64 {
927 self.a * self.d - self.b * self.c
928 }
929 pub fn inverse(&self) -> Option<AffineTransform2D> {
931 let det = self.determinant();
932 if det.abs() < 1e-15 {
933 return None;
934 }
935 let inv_det = 1.0 / det;
936 let a = self.d * inv_det;
937 let b = -self.b * inv_det;
938 let c = -self.c * inv_det;
939 let d = self.a * inv_det;
940 let e = -(a * self.e + b * self.f);
941 let f = -(c * self.e + d * self.f);
942 Some(AffineTransform2D { a, b, c, d, e, f })
943 }
944 pub fn contractivity(&self) -> f64 {
946 (self.a * self.a + self.b * self.b + self.c * self.c + self.d * self.d).sqrt()
947 }
948}
949#[derive(Debug, Clone, PartialEq, Eq)]
951pub enum OrbitType {
952 Escaping,
954 FixedPoint,
956 Periodic(usize),
958 Bounded,
960}
961pub struct KochSnowflake;
966impl KochSnowflake {
967 pub fn theoretical_dimension() -> f64 {
969 (4.0_f64).ln() / (3.0_f64).ln()
970 }
971 pub fn generate(generations: usize, side_length: f64) -> Vec<(Point2, Point2)> {
974 let h = side_length * (3.0_f64).sqrt() / 2.0;
975 let vertices = [
976 Point2::new(0.0, 0.0),
977 Point2::new(side_length, 0.0),
978 Point2::new(side_length / 2.0, h),
979 ];
980 let mut segments = Vec::new();
981 for i in 0..3 {
982 let a = vertices[i];
983 let b = vertices[(i + 1) % 3];
984 Self::subdivide(a, b, generations, &mut segments);
985 }
986 segments
987 }
988 fn subdivide(a: Point2, b: Point2, depth: usize, out: &mut Vec<(Point2, Point2)>) {
990 if depth == 0 {
991 out.push((a, b));
992 return;
993 }
994 let dx = b.x - a.x;
995 let dy = b.y - a.y;
996 let p1 = Point2::new(a.x + dx / 3.0, a.y + dy / 3.0);
997 let p3 = Point2::new(a.x + 2.0 * dx / 3.0, a.y + 2.0 * dy / 3.0);
998 let cos60 = 0.5_f64;
999 let sin60 = (3.0_f64).sqrt() / 2.0;
1000 let mx = dx / 3.0;
1001 let my = dy / 3.0;
1002 let apex = Point2::new(
1003 p1.x + mx * cos60 - my * sin60,
1004 p1.y + mx * sin60 + my * cos60,
1005 );
1006 Self::subdivide(a, p1, depth - 1, out);
1007 Self::subdivide(p1, apex, depth - 1, out);
1008 Self::subdivide(apex, p3, depth - 1, out);
1009 Self::subdivide(p3, b, depth - 1, out);
1010 }
1011 pub fn segment_count(n: usize) -> usize {
1013 3 * 4_usize.pow(n as u32)
1014 }
1015 pub fn perimeter(n: usize, side_length: f64) -> f64 {
1017 3.0 * side_length * (4.0 / 3.0_f64).powi(n as i32)
1018 }
1019 pub fn area(n: usize, side_length: f64) -> f64 {
1021 let a0 = (3.0_f64).sqrt() / 4.0 * side_length * side_length;
1022 let mut area = a0;
1023 for k in 1..=n {
1024 let num_new = 3 * 4_usize.pow((k - 1) as u32);
1025 let triangle_side = side_length / 3.0_f64.powi(k as i32);
1026 let triangle_area = (3.0_f64).sqrt() / 4.0 * triangle_side * triangle_side;
1027 area += num_new as f64 * triangle_area;
1028 }
1029 area
1030 }
1031}
1032pub struct Multifractal;
1038impl Multifractal {
1039 pub fn generalized_dimension(points: &[Point2], q: f64, num_scales: usize) -> (f64, f64) {
1045 if points.is_empty() || num_scales < 2 {
1046 return (0.0, 0.0);
1047 }
1048 let (min_x, max_x, min_y, max_y) = FractalDimension::bounding_box(points);
1049 let extent = (max_x - min_x).max(max_y - min_y).max(1e-15);
1050 let n_total = points.len() as f64;
1051 let mut log_eps = Vec::with_capacity(num_scales);
1052 let mut log_partition = Vec::with_capacity(num_scales);
1053 for i in 0..num_scales {
1054 let epsilon = extent / (2.0_f64).powi(i as i32 + 1);
1055 if epsilon < 1e-15 {
1056 break;
1057 }
1058 let mut box_counts: HashMap<(i64, i64), usize> = HashMap::new();
1059 for p in points {
1060 let ix = ((p.x - min_x) / epsilon).floor() as i64;
1061 let iy = ((p.y - min_y) / epsilon).floor() as i64;
1062 *box_counts.entry((ix, iy)).or_insert(0) += 1;
1063 }
1064 if (q - 1.0).abs() < 1e-10 {
1065 let mut entropy = 0.0;
1066 for &count in box_counts.values() {
1067 let pi = count as f64 / n_total;
1068 if pi > 0.0 {
1069 entropy -= pi * pi.ln();
1070 }
1071 }
1072 if entropy > 0.0 {
1073 log_eps.push(epsilon.ln());
1074 log_partition.push(entropy);
1075 }
1076 } else {
1077 let partition: f64 = box_counts
1078 .values()
1079 .map(|&count| (count as f64 / n_total).powf(q))
1080 .sum();
1081 if partition > 0.0 {
1082 log_eps.push(epsilon.ln());
1083 log_partition.push(partition.ln());
1084 }
1085 }
1086 }
1087 if log_eps.len() < 2 {
1088 return (0.0, 0.0);
1089 }
1090 let (slope, r_sq) = FractalDimension::linear_regression(&log_eps, &log_partition);
1091 if (q - 1.0).abs() < 1e-10 {
1092 (-slope, r_sq)
1093 } else {
1094 (slope / (q - 1.0), r_sq)
1095 }
1096 }
1097 pub fn spectrum(points: &[Point2], q_values: &[f64], num_scales: usize) -> Vec<(f64, f64)> {
1101 q_values
1102 .iter()
1103 .map(|&q| {
1104 let (dq, _) = Self::generalized_dimension(points, q, num_scales);
1105 (q, dq)
1106 })
1107 .collect()
1108 }
1109 pub fn singularity_spectrum(dq_spectrum: &[(f64, f64)]) -> Vec<(f64, f64)> {
1116 if dq_spectrum.len() < 3 {
1117 return Vec::new();
1118 }
1119 let tau: Vec<(f64, f64)> = dq_spectrum
1120 .iter()
1121 .map(|&(q, dq)| (q, (q - 1.0) * dq))
1122 .collect();
1123 let mut result = Vec::new();
1124 for i in 1..(tau.len() - 1) {
1125 let dq = tau[i + 1].0 - tau[i - 1].0;
1126 if dq.abs() < 1e-15 {
1127 continue;
1128 }
1129 let alpha = (tau[i + 1].1 - tau[i - 1].1) / dq;
1130 let f_alpha = tau[i].0 * alpha - tau[i].1;
1131 result.push((alpha, f_alpha));
1132 }
1133 result
1134 }
1135}
1136pub struct DragonCurve;
1138impl DragonCurve {
1139 pub fn lsystem() -> LSystem {
1141 LSystem::new(
1142 vec!['F', 'G', '+', '-'],
1143 vec![
1144 ProductionRule::new('F', "F+G"),
1145 ProductionRule::new('G', "F-G"),
1146 ],
1147 "F",
1148 )
1149 }
1150 pub fn generate(generations: usize, step_size: f64) -> Vec<(Point2, Point2)> {
1152 let ls = Self::lsystem();
1153 ls.turtle_interpret(generations, step_size, 90.0)
1154 }
1155}
1156pub struct BarnsleyFern;
1161impl BarnsleyFern {
1162 pub fn ifs() -> IteratedFunctionSystem {
1164 let transforms = vec![
1165 AffineTransform2D::new(0.0, 0.0, 0.0, 0.16, 0.0, 0.0),
1166 AffineTransform2D::new(0.85, 0.04, -0.04, 0.85, 0.0, 1.6),
1167 AffineTransform2D::new(0.20, -0.26, 0.23, 0.22, 0.0, 1.6),
1168 AffineTransform2D::new(-0.15, 0.28, 0.26, 0.24, 0.0, 0.44),
1169 ];
1170 let probabilities = vec![0.01, 0.85, 0.07, 0.07];
1171 IteratedFunctionSystem::new(transforms, probabilities)
1172 }
1173 pub fn generate(num_points: usize) -> Vec<Point2> {
1175 let ifs = Self::ifs();
1176 ifs.chaos_game(Point2::new(0.0, 0.0), num_points + 200, 200)
1177 }
1178}
1179#[derive(Debug, Clone, Copy, PartialEq)]
1181pub struct Point3 {
1182 pub x: f64,
1184 pub y: f64,
1186 pub z: f64,
1188}
1189impl Point3 {
1190 pub fn new(x: f64, y: f64, z: f64) -> Self {
1192 Self { x, y, z }
1193 }
1194 pub fn distance(&self, other: &Point3) -> f64 {
1196 ((self.x - other.x).powi(2) + (self.y - other.y).powi(2) + (self.z - other.z).powi(2))
1197 .sqrt()
1198 }
1199}
1200#[derive(Debug, Clone)]
1206pub struct LSystem {
1207 pub alphabet: Vec<char>,
1209 pub rules: HashMap<char, String>,
1211 pub axiom: String,
1213 pub angle_deg: f64,
1215 pub step_size: f64,
1217}
1218#[derive(Debug, Clone)]
1220pub struct LSystemEvolution {
1221 pub string: String,
1223 pub generation: usize,
1225}
1226impl LSystem {
1227 pub fn new(alphabet: Vec<char>, rules: Vec<ProductionRule>, axiom: impl Into<String>) -> Self {
1229 let mut rule_map = HashMap::new();
1230 for rule in rules {
1231 rule_map.insert(rule.predecessor, rule.successor);
1232 }
1233 Self {
1234 alphabet,
1235 rules: rule_map,
1236 axiom: axiom.into(),
1237 angle_deg: 90.0,
1238 step_size: 1.0,
1239 }
1240 }
1241 pub fn koch_curve() -> Self {
1243 let mut ls = Self::new(vec!['F'], vec![ProductionRule::new('F', "F+F-F-F+F")], "F");
1244 ls.angle_deg = 90.0;
1245 ls
1246 }
1247 pub fn sierpinski() -> Self {
1249 let mut ls = Self::new(
1250 vec!['A', 'B'],
1251 vec![
1252 ProductionRule::new('A', "B-A-B"),
1253 ProductionRule::new('B', "A+B+A"),
1254 ],
1255 "A",
1256 );
1257 ls.angle_deg = 60.0;
1258 ls
1259 }
1260 pub fn dragon_curve() -> Self {
1262 let mut ls = Self::new(
1263 vec!['X', 'Y'],
1264 vec![
1265 ProductionRule::new('X', "X+YF+"),
1266 ProductionRule::new('Y', "-FX-Y"),
1267 ],
1268 "FX",
1269 );
1270 ls.angle_deg = 90.0;
1271 ls
1272 }
1273 pub fn plant_branching() -> Self {
1275 let mut ls = Self::new(
1276 vec!['X', 'F'],
1277 vec![
1278 ProductionRule::new('X', "F+[[X]-X]-F[-FX]+X"),
1279 ProductionRule::new('F', "FF"),
1280 ],
1281 "X",
1282 );
1283 ls.angle_deg = 25.0;
1284 ls
1285 }
1286 pub fn evolve(&self, n: usize) -> LSystemEvolution {
1288 LSystemEvolution {
1289 string: self.iterate(n),
1290 generation: n,
1291 }
1292 }
1293 pub fn interpret(&self, state: &LSystemEvolution) -> Vec<(Point2, Point2)> {
1295 let angle_rad = self.angle_deg * std::f64::consts::PI / 180.0;
1296 let step = self.step_size;
1297 let mut x = 0.0_f64;
1298 let mut y = 0.0_f64;
1299 let mut heading = 0.0_f64;
1300 let mut stack: Vec<(f64, f64, f64)> = Vec::new();
1301 let mut segments = Vec::new();
1302 for ch in state.string.chars() {
1303 match ch {
1304 'F' | 'G' | 'A' | 'B' => {
1305 let nx = x + step * heading.cos();
1306 let ny = y + step * heading.sin();
1307 segments.push((Point2::new(x, y), Point2::new(nx, ny)));
1308 x = nx;
1309 y = ny;
1310 }
1311 '+' => heading += angle_rad,
1312 '-' => heading -= angle_rad,
1313 '[' => stack.push((x, y, heading)),
1314 ']' => {
1315 if let Some((sx, sy, sh)) = stack.pop() {
1316 x = sx;
1317 y = sy;
1318 heading = sh;
1319 }
1320 }
1321 _ => {}
1322 }
1323 }
1324 segments
1325 }
1326 pub fn iterate(&self, n: usize) -> String {
1328 let mut current = self.axiom.clone();
1329 for _ in 0..n {
1330 let mut next = String::with_capacity(current.len() * 2);
1331 for ch in current.chars() {
1332 if let Some(replacement) = self.rules.get(&ch) {
1333 next.push_str(replacement);
1334 } else {
1335 next.push(ch);
1336 }
1337 }
1338 current = next;
1339 }
1340 current
1341 }
1342 pub fn iterate_length(&self, n: usize) -> usize {
1344 let mut lengths: HashMap<char, usize> = HashMap::new();
1345 for &ch in &self.alphabet {
1346 lengths.insert(ch, 1);
1347 }
1348 for _ in 0..n {
1349 let mut new_lengths: HashMap<char, usize> = HashMap::new();
1350 for &ch in &self.alphabet {
1351 if let Some(replacement) = self.rules.get(&ch) {
1352 let len: usize = replacement
1353 .chars()
1354 .map(|c| lengths.get(&c).copied().unwrap_or(1))
1355 .sum();
1356 new_lengths.insert(ch, len);
1357 } else {
1358 new_lengths.insert(ch, *lengths.get(&ch).unwrap_or(&1));
1359 }
1360 }
1361 lengths = new_lengths;
1362 }
1363 self.axiom
1364 .chars()
1365 .map(|c| lengths.get(&c).copied().unwrap_or(1))
1366 .sum()
1367 }
1368 pub fn turtle_interpret(
1378 &self,
1379 n: usize,
1380 step_size: f64,
1381 angle_deg: f64,
1382 ) -> Vec<(Point2, Point2)> {
1383 let string = self.iterate(n);
1384 let angle_rad = angle_deg * std::f64::consts::PI / 180.0;
1385 let mut x = 0.0_f64;
1386 let mut y = 0.0_f64;
1387 let mut heading = 0.0_f64;
1388 let mut stack: Vec<(f64, f64, f64)> = Vec::new();
1389 let mut segments = Vec::new();
1390 for ch in string.chars() {
1391 match ch {
1392 'F' | 'G' => {
1393 let nx = x + step_size * heading.cos();
1394 let ny = y + step_size * heading.sin();
1395 segments.push((Point2::new(x, y), Point2::new(nx, ny)));
1396 x = nx;
1397 y = ny;
1398 }
1399 '+' => {
1400 heading += angle_rad;
1401 }
1402 '-' => {
1403 heading -= angle_rad;
1404 }
1405 '[' => {
1406 stack.push((x, y, heading));
1407 }
1408 ']' => {
1409 if let Some((sx, sy, sh)) = stack.pop() {
1410 x = sx;
1411 y = sy;
1412 heading = sh;
1413 }
1414 }
1415 _ => {}
1416 }
1417 }
1418 segments
1419 }
1420 pub fn add_rule(&mut self, predecessor: char, successor: impl Into<String>) {
1422 let succ: String = successor.into();
1423 self.rules.insert(predecessor, succ);
1424 if !self.alphabet.contains(&predecessor) {
1425 self.alphabet.push(predecessor);
1426 }
1427 }
1428}
1429pub struct JuliaSet {
1433 pub cr: f64,
1435 pub ci: f64,
1437 pub max_iter: u32,
1439 pub escape_radius_sq: f64,
1441}
1442impl JuliaSet {
1443 pub fn new(cr: f64, ci: f64, max_iter: u32, escape_radius: f64) -> Self {
1445 Self {
1446 cr,
1447 ci,
1448 max_iter,
1449 escape_radius_sq: escape_radius * escape_radius,
1450 }
1451 }
1452 pub fn classic() -> Self {
1454 Self::new(-0.7, 0.27015, 256, 2.0)
1455 }
1456 pub fn dendrite() -> Self {
1458 Self::new(0.0, 1.0, 256, 2.0)
1459 }
1460 pub fn escape_time(&self, zr: f64, zi: f64) -> Option<u32> {
1462 let mut zr = zr;
1463 let mut zi = zi;
1464 for i in 0..self.max_iter {
1465 let zr2 = zr * zr;
1466 let zi2 = zi * zi;
1467 if zr2 + zi2 > self.escape_radius_sq {
1468 return Some(i);
1469 }
1470 let new_zi = 2.0 * zr * zi + self.ci;
1471 zr = zr2 - zi2 + self.cr;
1472 zi = new_zi;
1473 }
1474 None
1475 }
1476 pub fn is_in_set(&self, zr: f64, zi: f64) -> bool {
1478 self.escape_time(zr, zi).is_none()
1479 }
1480 pub fn classify_orbit(&self, zr: f64, zi: f64) -> OrbitType {
1482 let mut zr = zr;
1483 let mut zi = zi;
1484 let mut history_r = Vec::with_capacity(self.max_iter as usize);
1485 let mut history_i = Vec::with_capacity(self.max_iter as usize);
1486 for _ in 0..self.max_iter {
1487 let zr2 = zr * zr;
1488 let zi2 = zi * zi;
1489 if zr2 + zi2 > self.escape_radius_sq {
1490 return OrbitType::Escaping;
1491 }
1492 history_r.push(zr);
1493 history_i.push(zi);
1494 let new_zi = 2.0 * zr * zi + self.ci;
1495 zr = zr2 - zi2 + self.cr;
1496 zi = new_zi;
1497 }
1498 let n = history_r.len();
1499 if n < 2 {
1500 return OrbitType::Bounded;
1501 }
1502 let last_r = history_r[n - 1];
1503 let last_i = history_i[n - 1];
1504 for period in 1..=16.min(n - 1) {
1505 let prev_r = history_r[n - 1 - period];
1506 let prev_i = history_i[n - 1 - period];
1507 let dr = (last_r - prev_r).abs();
1508 let di = (last_i - prev_i).abs();
1509 if dr < 1e-10 && di < 1e-10 {
1510 if period == 1 {
1511 return OrbitType::FixedPoint;
1512 }
1513 return OrbitType::Periodic(period);
1514 }
1515 }
1516 OrbitType::Bounded
1517 }
1518 pub fn smooth_escape_time(&self, zr: f64, zi: f64) -> f64 {
1520 let mut zr = zr;
1521 let mut zi = zi;
1522 for i in 0..self.max_iter {
1523 let zr2 = zr * zr;
1524 let zi2 = zi * zi;
1525 if zr2 + zi2 > self.escape_radius_sq {
1526 let modulus = (zr2 + zi2).sqrt();
1527 let mu = i as f64 - modulus.ln().ln() / (2.0_f64).ln();
1528 return mu;
1529 }
1530 let new_zi = 2.0 * zr * zi + self.ci;
1531 zr = zr2 - zi2 + self.cr;
1532 zi = new_zi;
1533 }
1534 self.max_iter as f64
1535 }
1536 pub fn render_grid(
1538 &self,
1539 x_min: f64,
1540 x_max: f64,
1541 y_min: f64,
1542 y_max: f64,
1543 width: usize,
1544 height: usize,
1545 ) -> Vec<Vec<Option<u32>>> {
1546 let dx = (x_max - x_min) / width as f64;
1547 let dy = (y_max - y_min) / height as f64;
1548 (0..height)
1549 .map(|j| {
1550 let zi = y_min + (j as f64 + 0.5) * dy;
1551 (0..width)
1552 .map(|i| {
1553 let zr = x_min + (i as f64 + 0.5) * dx;
1554 self.escape_time(zr, zi)
1555 })
1556 .collect()
1557 })
1558 .collect()
1559 }
1560}
1561#[derive(Debug, Clone, Copy, PartialEq)]
1563pub struct Aabb3 {
1564 pub min: Point3,
1566 pub max: Point3,
1568}
1569impl Aabb3 {
1570 pub fn new(min: Point3, max: Point3) -> Self {
1572 Self { min, max }
1573 }
1574 pub fn side(&self) -> f64 {
1576 self.max.x - self.min.x
1577 }
1578 pub fn volume(&self) -> f64 {
1580 (self.max.x - self.min.x) * (self.max.y - self.min.y) * (self.max.z - self.min.z)
1581 }
1582 pub fn center(&self) -> Point3 {
1584 Point3::new(
1585 (self.min.x + self.max.x) / 2.0,
1586 (self.min.y + self.max.y) / 2.0,
1587 (self.min.z + self.max.z) / 2.0,
1588 )
1589 }
1590}
1591pub struct MortonCurve;
1596impl MortonCurve {
1597 pub fn encode(x: u32, y: u32) -> u64 {
1601 Self::spread_bits(x as u64) | (Self::spread_bits(y as u64) << 1)
1602 }
1603 pub fn decode(code: u64) -> (u32, u32) {
1605 let x = Self::compact_bits(code) as u32;
1606 let y = Self::compact_bits(code >> 1) as u32;
1607 (x, y)
1608 }
1609 fn spread_bits(mut v: u64) -> u64 {
1611 v &= 0x0000_0000_FFFF_FFFF;
1612 v = (v | (v << 16)) & 0x0000_FFFF_0000_FFFF;
1613 v = (v | (v << 8)) & 0x00FF_00FF_00FF_00FF;
1614 v = (v | (v << 4)) & 0x0F0F_0F0F_0F0F_0F0F;
1615 v = (v | (v << 2)) & 0x3333_3333_3333_3333;
1616 v = (v | (v << 1)) & 0x5555_5555_5555_5555;
1617 v
1618 }
1619 fn compact_bits(mut v: u64) -> u64 {
1621 v &= 0x5555_5555_5555_5555;
1622 v = (v | (v >> 1)) & 0x3333_3333_3333_3333;
1623 v = (v | (v >> 2)) & 0x0F0F_0F0F_0F0F_0F0F;
1624 v = (v | (v >> 4)) & 0x00FF_00FF_00FF_00FF;
1625 v = (v | (v >> 8)) & 0x0000_FFFF_0000_FFFF;
1626 v = (v | (v >> 16)) & 0x0000_0000_FFFF_FFFF;
1627 v
1628 }
1629 pub fn generate_path(n: u32) -> Vec<(u32, u32)> {
1633 let total = n * n;
1634 let mut pairs: Vec<(u64, u32, u32)> = Vec::with_capacity(total as usize);
1635 for y in 0..n {
1636 for x in 0..n {
1637 pairs.push((Self::encode(x, y), x, y));
1638 }
1639 }
1640 pairs.sort_by_key(|&(code, _, _)| code);
1641 pairs.iter().map(|&(_, x, y)| (x, y)).collect()
1642 }
1643 pub fn encode_3d(x: u32, y: u32, z: u32) -> u64 {
1645 Self::spread_bits_3d(x as u64)
1646 | (Self::spread_bits_3d(y as u64) << 1)
1647 | (Self::spread_bits_3d(z as u64) << 2)
1648 }
1649 fn spread_bits_3d(mut v: u64) -> u64 {
1651 v &= 0x001F_FFFF;
1652 v = (v | (v << 32)) & 0x001F_0000_0000_FFFF;
1653 v = (v | (v << 16)) & 0x001F_0000_FF00_00FF;
1654 v = (v | (v << 8)) & 0x100F_00F0_0F00_F00F;
1655 v = (v | (v << 4)) & 0x10C3_0C30_C30C_30C3;
1656 v = (v | (v << 2)) & 0x1249_2492_4924_9249;
1657 v
1658 }
1659}
1660#[derive(Debug, Clone, Copy, PartialEq)]
1662pub struct Point2 {
1663 pub x: f64,
1665 pub y: f64,
1667}
1668impl Point2 {
1669 pub fn new(x: f64, y: f64) -> Self {
1671 Self { x, y }
1672 }
1673 pub fn distance(&self, other: &Point2) -> f64 {
1675 ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
1676 }
1677 pub fn distance_sq(&self, other: &Point2) -> f64 {
1679 (self.x - other.x).powi(2) + (self.y - other.y).powi(2)
1680 }
1681}
1682pub struct MengerSponge;
1688impl MengerSponge {
1689 pub fn theoretical_dimension() -> f64 {
1691 (20.0_f64).ln() / (3.0_f64).ln()
1692 }
1693 pub fn generate(level: usize, origin: Point3, size: f64) -> Vec<Aabb3> {
1697 if level == 0 {
1698 return vec![Aabb3::new(
1699 origin,
1700 Point3::new(origin.x + size, origin.y + size, origin.z + size),
1701 )];
1702 }
1703 let sub = size / 3.0;
1704 let mut cubes = Vec::new();
1705 for ix in 0..3_u8 {
1706 for iy in 0..3_u8 {
1707 for iz in 0..3_u8 {
1708 let center_count = u8::from(ix == 1) + u8::from(iy == 1) + u8::from(iz == 1);
1709 if center_count >= 2 {
1710 continue;
1711 }
1712 let o = Point3::new(
1713 origin.x + ix as f64 * sub,
1714 origin.y + iy as f64 * sub,
1715 origin.z + iz as f64 * sub,
1716 );
1717 cubes.extend(Self::generate(level - 1, o, sub));
1718 }
1719 }
1720 }
1721 cubes
1722 }
1723 pub fn cube_count(level: usize) -> usize {
1725 20_usize.pow(level as u32)
1726 }
1727 pub fn total_volume(level: usize) -> f64 {
1729 (20.0 / 27.0_f64).powi(level as i32)
1730 }
1731}
1732pub struct KochCurve;
1737impl KochCurve {
1738 pub fn lsystem() -> LSystem {
1740 LSystem::new(
1741 vec!['F', '+', '-'],
1742 vec![ProductionRule::new('F', "F+F--F+F")],
1743 "F",
1744 )
1745 }
1746 pub fn theoretical_dimension() -> f64 {
1748 (4.0_f64).ln() / (3.0_f64).ln()
1749 }
1750 pub fn generate(generations: usize, step_size: f64) -> Vec<(Point2, Point2)> {
1752 let ls = Self::lsystem();
1753 ls.turtle_interpret(generations, step_size, 60.0)
1754 }
1755 pub fn segment_count(n: usize) -> usize {
1757 4_usize.pow(n as u32)
1758 }
1759}