random_fractal_generator/
random_fractal_generator.rs1#![allow(clippy::clone_double_ref)]
2
3use image::Rgb;
4use rand::seq::SliceRandom;
5use rand::{thread_rng, Rng};
6
7use dcc_lsystem::renderer::ImageRendererOptionsBuilder;
8use dcc_lsystem::renderer::Renderer;
9use dcc_lsystem::turtle::{TurtleAction, TurtleLSystemBuilder};
10
11fn valid_rule(rule: &[&str]) -> bool {
12 if rule.is_empty() {
13 return false;
14 }
15
16 let r = rule.join("");
17
18 if r.contains("+-") || !r.contains('+') {
19 return false;
20 }
21
22 let mut level = 0;
23
24 for c in rule {
25 if c == &"+" {
26 level += 1;
27 } else if c == &"-" {
28 if level == 0 {
29 return false;
30 }
31 level -= 1;
32 }
33 }
34
35 level == 0
36}
37
38pub fn main() {
39 'processing: loop {
40 let mut rng = thread_rng();
43
44 let axiom_length = rng.gen_range(0..=2);
46 let mut axiom = vec!["X"];
47 let choices = ["L", "R", "F", "X", "Y"];
48 let weighted_choices = [
49 ("F", rng.gen_range(1..=8)),
50 ("X", rng.gen_range(2..=4)),
51 ("Y", rng.gen_range(2..=4)),
52 ("L", rng.gen_range(2..=6)),
53 ("R", rng.gen_range(2..=6)),
54 ("+", rng.gen_range(4..=8)),
55 ("-", rng.gen_range(4..=8)),
56 ];
57
58 for _ in 0..axiom_length {
59 axiom.push(choices.choose(&mut rng).unwrap().clone());
60 }
61
62 let mut x_rule = Vec::new();
64
65 while !valid_rule(&x_rule) {
66 x_rule.clear();
67
68 let x_rule_length = rng.gen_range(4..=10);
69
70 for _ in 0..x_rule_length {
71 x_rule.push(
72 weighted_choices
73 .choose_weighted(&mut rng, |item| item.1)
74 .unwrap()
75 .0
76 .clone(),
77 );
78 }
79 }
80
81 let mut y_rule = Vec::new();
82
83 while !valid_rule(&y_rule) {
84 y_rule.clear();
85 let y_rule_length = rng.gen_range(4..=10);
87
88 for _ in 0..y_rule_length {
89 y_rule.push(
90 weighted_choices
91 .choose_weighted(&mut rng, |item| item.1)
92 .unwrap()
93 .0
94 .clone(),
95 );
96 }
97 }
98
99 let mut builder = TurtleLSystemBuilder::new();
100
101 builder
103 .token("L", TurtleAction::Rotate(25))
104 .token("R", TurtleAction::Rotate(-25))
105 .token("F", TurtleAction::Forward(100))
106 .token("+", TurtleAction::Push)
107 .token("-", TurtleAction::Pop)
108 .token("X", TurtleAction::Nothing)
109 .token("Y", TurtleAction::Nothing)
110 .axiom(&axiom.join(" "))
111 .rule(format!("X => {}", x_rule.join(" ")).as_str())
112 .rule(format!("Y => {}", y_rule.join(" ")).as_str());
113
114 let (mut system, renderer) = builder.finish();
116
117 let options = ImageRendererOptionsBuilder::new()
118 .padding(20)
119 .thickness(1.0)
120 .fill_color(Rgb([0u8, 0u8, 0u8]))
121 .line_color(Rgb([218u8, 112u8, 214u8]))
122 .build();
123
124 system.step_by(10);
126
127 let buffer = renderer.render(&system, &options);
129
130 if buffer.width() < 1000 || buffer.height() < 1000 {
131 continue 'processing;
132 }
133
134 buffer.save("test_render.png").expect("Saving file failed");
135
136 break;
137 }
138}