use regex::{Regex, RegexBuilder, Captures}; use std::sync::RwLock; use crate::instruction::{Instruction, InstructionType, Vector3, CameraData}; lazy_static!{ static ref REGEX_CACHE: RwLock = RwLock::new(RegexPatterns{ single_instruction: RegexBuilder::new(r"(\w+)\s*\((\d+(?:\.\d+)?),?\s*(\d+(?:\.\d+)?),?\s*(\d+(?:\.\d+)?)(?:,?\s*(\d+(?:\.\d+)?))?\s*\)").case_insensitive(true).build().unwrap(), //multi_instruction: RegexBuilder::new("").case_insensitive(true).build().unwrap(), }); } struct RegexPatterns { single_instruction: Regex, } impl Instruction { pub fn parse_line(line: &str) -> Result { let cache_lock = REGEX_CACHE.read().unwrap(); if count(line, '(') > 1 { // do multi parsing let mut instructions = Vec::::new(); for capture in cache_lock.single_instruction.captures_iter(line) { let ins = Self::parse_single(&capture)?; instructions.push(ins); } return Ok(Instruction { instr: InstructionType::Multi{instructions}, start: 0.0, progress: 0.0, base: CameraData::default(), }) } else { // assume single instruction on line if let Some(captures) = cache_lock.single_instruction.captures(line) { let instr = Self::parse_single(&captures)?; return Ok(Instruction { instr, start: 0.0, progress: 0.0, base: CameraData::default(), }) } else { return Err("Invalid line".to_string()); } } } fn parse_single(c: &Captures) -> Result { let name = c.get(1).unwrap().as_str(); let param1 = c.get(2).unwrap().as_str().parse::().unwrap(); let param2 = c.get(3).unwrap().as_str().parse::().unwrap(); let param3 = c.get(4).unwrap().as_str().parse::().unwrap(); // optional 4th param // let param4 = c.get(5).unwrap().as_str().parse::(); match name.to_uppercase().as_str() { "TRACK" => { if let Some(param4) = c.get(5) { Ok(InstructionType::Track { vector: Vector3{x: param1, y: param2, z: param3}, time: param4.as_str().parse::().unwrap(), }) } else { Ok(InstructionType::Track { vector: Vector3{x: param1, y: param2, z: param3}, time: 0.0, }) } }, "MOVE" => Ok(InstructionType::Move { vector: Vector3{x: param1, y: param2, z: param3}, }), "ROTATE" => { if let Some(param4) = c.get(5) { Ok(InstructionType::Rotate { vector: Vector3{x: param1, y: param2, z: param3}, time: param4.as_str().parse::().unwrap(), }) } else { Ok(InstructionType::Track { vector: Vector3{x: param1, y: param2, z: param3}, time: 0.0, }) } }, "LOOK" => Ok(InstructionType::Look { vector: Vector3{x: param1, y: param2, z: param3}, }), _ => Err(format!("Invalid instruction {}", name)) } } pub fn start(&mut self, now: f64, base: CameraData) { self.start = now; self.base = base; } pub fn lerp(&mut self, now: f64) -> CameraData { self.progress += now; match self.instr { InstructionType::Look {..} | InstructionType::Move {..} => self.instr.lerp(self.start, now), InstructionType::Track {..} | InstructionType::Rotate {..} | InstructionType::Multi{..} => self.base + self.instr.lerp(self.start, now), } } pub fn done(&self) -> bool { !(self.progress < (self.start + self.instr.time())) } } // string helper functions fn count(target: &str, chr: char) -> usize { let mut times: usize = 0; for c in target.chars() { if chr == c { times+=1; } } times }