use crate::geometry::{Path, Point, Vector};
use crate::prelude::*;
use crate::system::family::get_or_init_family;
use crate::interpretation::Interpretation;
use crate::interpretation::svg::SvgPathInterpretation;
use crate::symbols::SymbolStore;
pub mod parser;
pub fn abop_family() -> SystemFamily {
SystemFamily::define()
.with_terminal("[", Some("Start a branch"))
.with_terminal("]", Some("Finish a branch"))
.with_terminal("+", Some("Turn turtle right"))
.with_terminal("-", Some("Turn turtle left"))
.with_production("Forward", Some("Move the turtle forward, drawing a line"))
.with_production("Move", Some("Move the turtle forward WITHOUT drawing"))
.with_production("X", Some("A growth point for the plant / branch"))
.build("ABOP")
.unwrap()
}
#[derive(Debug, Clone)]
pub struct AbopTurtleInterpretation {
n: usize,
delta: f32
}
impl Default for AbopTurtleInterpretation {
fn default() -> Self {
AbopTurtleInterpretation::new(5, 22.5)
}
}
impl AbopTurtleInterpretation {
pub fn new(n: usize, delta: f32) -> Self {
Self {
n,
delta
}
}
pub fn n(&self) -> usize {
self.n
}
pub fn delta(&self) -> f32 {
self.delta
}
}
pub type AbopSvgInterpretation = SvgPathInterpretation<AbopTurtleInterpretation>;
impl Interpretation for AbopTurtleInterpretation {
type Item = Vec<Path>;
fn system() -> crate::Result<System> {
let family = get_or_init_family("ABOP", abop_family);
System::of_family(family)
}
fn interpret<S: SymbolStore>(&self,
tokens: &S,
string: &ProductionString) -> crate::Result<Self::Item> {
let forward = tokens.get_symbol("Forward").unwrap();
let space = tokens.get_symbol("Move").unwrap();
let right = tokens.get_symbol("+").unwrap();
let left = tokens.get_symbol("-").unwrap();
let push = tokens.get_symbol("[").unwrap();
let pop = tokens.get_symbol("]").unwrap();
let mut pos_stack: Vec<(Point, Vector)> = Vec::new();
let mut pos = Point::zero();
let mut dir = Vector::new(0.0, 5.0);
let angle: f64 = self.delta() as f64;
let mut paths: Vec<Path> = Vec::new();
let mut path = Path::new();
path.push(pos);
for token in string {
if token == forward { pos = pos + dir;
path.push(pos);
} else if token == space {
pos = pos + dir;
if path.len() > 1 {
paths.push(path)
}
path = Path::new();
path.push(pos);
} else if token == push { pos_stack.push((pos, dir));
} else if token == pop { (pos, dir) = pos_stack.pop().expect("Nothing to pop");
if path.len() > 1 {
paths.push(path)
}
path = Path::new();
path.push(pos);
} else if token == left { dir = dir.rotate(-angle);
} else if token == right { dir = dir.rotate(angle);
}
}
if !path.is_empty() {
paths.push(path)
}
Ok(paths)
}
fn run_settings(&self) -> RunSettings {
#[allow(clippy::needless_update)]
RunSettings {
max_iterations: self.n,
..RunSettings::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::interpretation::Interpretation;
use crate::parser::parse_prod_string;
#[test]
fn geometry_interpretation() {
let system = AbopTurtleInterpretation::system().unwrap();
system.parse_production("Forward -> Forward Forward").unwrap();
let string = parse_prod_string("Forward").unwrap();
let string = system.derive_once(string).unwrap();
assert_eq!(string.len(), 2);
let result = AbopTurtleInterpretation::default_interpret(&system, &string).unwrap();
assert_eq!(result.len(), 1);
let result = result[0].clone();
assert_eq!(result.len(), 3)
}
}