use std::{collections::HashMap, fmt::Display};
use crate::rng::SystemRng;
use rand::{seq::SliceRandom, SeedableRng};
use rustc_hash::FxHashMap;
#[derive(Debug, Clone)]
pub struct LSystem {
pub axiom: String,
pub rules: FxHashMap<char, String>,
}
impl LSystem {
pub fn new<S: ToString>(axiom: String, rules: &[(char, S)]) -> LSystem {
let mut map = FxHashMap::default();
for rule in rules {
map.insert(rule.0, rule.1.to_string());
}
LSystem { axiom, rules: map }
}
pub fn get(&self, c: &char) -> Option<&String> {
self.rules.get(c)
}
pub fn builder(&self, depth: usize) -> LSystemBuilder {
LSystemBuilder::new(&self, depth)
}
pub fn string(&self, depth: usize) -> String {
let mut expression = self.axiom.clone();
for _ in 0..depth {
let mut new = String::new();
for c in expression.chars() {
if let Some(s) = self.rules.get(&c) {
new.push_str(s)
} else {
new.push(c)
}
}
expression = new;
}
expression
}
}
impl Display for LSystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut out = format!("Axiom: {}\nRules:\n", self.axiom);
for i in self
.rules
.iter()
.map(|(k, v)| format!(" {} => {}\n", k, v))
{
out.push_str(&i);
}
write!(f, "{}", out)
}
}
#[derive(Debug, Clone)]
pub struct LSystemBuilder<'a> {
system: &'a LSystem,
depth: usize,
layers: Vec<std::str::Chars<'a>>,
active_layer: usize,
}
impl<'a> LSystemBuilder<'a> {
pub fn new(system: &'a LSystem, depth: usize) -> Self {
let mut layers = vec!["".chars(); depth + 1];
layers[depth] = system.axiom.chars();
Self {
system,
depth,
layers,
active_layer: depth - 1,
}
}
}
impl<'a> Iterator for LSystemBuilder<'_> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.active_layer > self.depth {
return None;
} else {
if let Some(c) = self.layers[0].next() {
return Some(c);
} else {
if let Some(c) = self.layers[self.active_layer].next() {
match self.system.get(&c) {
None => return Some(c),
Some(cs) => self.layers[self.active_layer - 1] = cs.chars(),
}
self.active_layer -= 1
} else {
self.active_layer += 1
}
}
}
}
}
}
#[derive(Debug, Clone)]
pub struct LSystemStochastic {
pub axiom: String,
pub rules: HashMap<char, Vec<(String, f32)>>,
}
impl LSystemStochastic {
pub fn new<S: ToString>(axiom: String, rules: &[(char, Vec<(S, f32)>)]) -> LSystemStochastic {
let mut map = HashMap::with_capacity(rules.len());
for rule in rules {
map.insert(
rule.0,
rule.1.iter().map(|(s, n)| (s.to_string(), *n)).collect(),
);
}
LSystemStochastic { axiom, rules: map }
}
pub fn get(&self, c: &char, rng: &mut SystemRng) -> Option<&String> {
if let Some(s) = self.rules.get(c) {
match s.choose_weighted(rng, |item| item.1) {
Ok(s) => Some(&s.0),
Err(e) => panic!("{}", e.to_string()),
}
} else {
None
}
}
pub fn builder(&self, depth: usize, seed: Option<u64>) -> LSystemBuilderStochastic {
LSystemBuilderStochastic::new(&self, depth, seed)
}
pub fn string(&self, depth: usize, seed: Option<u64>) -> String {
let mut expression = self.axiom.clone();
let mut rng = match seed {
Some(n) => SystemRng::seed_from_u64(n),
None => SystemRng::from_entropy(),
};
for _ in 0..depth {
let mut new = String::new();
for c in expression.chars() {
if let Some(s) = self.rules.get(&c) {
match s.choose_weighted(&mut rng, |item| item.1) {
Ok(pair) => new.push_str(&pair.0),
Err(e) => panic!("{}", e.to_string()),
}
} else {
new.push(c)
}
}
expression = new;
}
expression
}
}
impl Display for LSystemStochastic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut out = format!("Axiom: {}\nRules", self.axiom);
let mut rule_strings = Vec::new();
for (c, replacements) in self.rules.iter() {
let mut s = String::new();
if replacements.len() == 1 {
let rep = &replacements[0].0;
s.push_str(&format!("\n {c} => {rep}"));
} else {
s.push_str(&format!("\n {c} =>"));
let norm: f32 = replacements.iter().map(|x| x.1).sum();
for (rep, prob) in replacements {
let normed_prob = prob / norm;
s.push_str(&format!("\n {rep} ({normed_prob:.2})"));
}
}
rule_strings.push(s);
}
rule_strings.sort();
for s in rule_strings {
out.push_str(&s)
}
write!(f, "{}", out)
}
}
#[derive(Debug, Clone)]
pub struct LSystemBuilderStochastic<'a> {
system: &'a LSystemStochastic,
depth: usize,
layers: Vec<std::str::Chars<'a>>,
active_layer: usize,
rng: SystemRng,
}
impl<'a> LSystemBuilderStochastic<'a> {
pub fn new(system: &'a LSystemStochastic, depth: usize, seed: Option<u64>) -> Self {
let mut layers = vec!["".chars(); depth + 1];
layers[depth] = system.axiom.chars();
let rng = match seed {
Some(n) => SystemRng::seed_from_u64(n),
None => SystemRng::from_entropy(),
};
Self {
system,
depth,
layers,
active_layer: depth - 1,
rng,
}
}
}
impl<'a> Iterator for LSystemBuilderStochastic<'_> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.active_layer > self.depth {
return None;
} else {
if let Some(c) = self.layers[0].next() {
return Some(c);
} else {
if let Some(c) = self.layers[self.active_layer].next() {
match self.system.get(&c, &mut self.rng) {
None => return Some(c),
Some(cs) => self.layers[self.active_layer - 1] = cs.chars(),
}
self.active_layer -= 1
} else {
self.active_layer += 1
}
}
}
}
}
}
#[test]
fn display_test() {
use crate::builder::LSystem;
let axiom = String::from("X");
let rules = [('X', "F[X][+DX]-DX"), ('D', "F")];
let system = LSystem::new(axiom, &rules);
assert_eq!(
"Axiom: X\nRules:\n X => F[X][+DX]-DX\n D => F\n",
system.to_string()
);
}
#[test]
fn validity_test() {
use crate::builder::LSystem;
let axiom = String::from("X");
let rules = [('X', "F[X][+DX]-DX"), ('D', "F")];
let system = LSystem::new(axiom, &rules);
let depth = 3;
let s = system.string(depth);
let e = system.builder(depth);
assert!(e.zip(s.chars()).all(|(a, b)| a == b))
}
#[test]
fn stochastic_display_test() {
let axiom = String::from("X");
let rules = [
('X', vec![("F[X][+DX]-DX", 1.0_f32)]),
('D', vec![("F", 2.0), ("FF", 1.0), ("D", 1.0)]),
];
let system = LSystemStochastic::new(axiom, &rules);
println!("{}", system)
}