diff --git a/Cargo.lock b/Cargo.lock index d9ef259..3c0e0fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,7 +311,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "voxelmap" -version = "0.4.0" +version = "0.4.2" dependencies = [ "clap", "lodepng", diff --git a/Cargo.toml b/Cargo.toml index d5d1089..512d987 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "voxelmap" -version = "0.4.0" +version = "0.4.2" authors = ["raffitz "] edition = "2018" description = "Creates voxel maps to build voxelised objects" diff --git a/src/astree.rs b/src/astree.rs index f97145d..f5ef4f6 100644 --- a/src/astree.rs +++ b/src/astree.rs @@ -2,7 +2,7 @@ use crate::error::Error; use std::collections::{HashMap, HashSet}; use std::io::Write; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum FunctionType { Sin, Cos, @@ -22,7 +22,7 @@ pub enum FunctionType { Neg, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FunctionData { kind: FunctionType, arg: Box, @@ -91,7 +91,7 @@ impl FunctionData { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct OperationData { kind: char, left: Box, @@ -142,7 +142,7 @@ impl OperationData { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Expression { Var(char), Float(f64), diff --git a/src/main.rs b/src/main.rs index 4aa4b21..490bc56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,15 +11,9 @@ mod error; mod ngon; mod parser; -macro_rules! scale_message { - ($n:ident) => { - Err(format!("<{}> is not a valid scale value", $n)) - }; -} - -macro_rules! sides_message { - ($n:ident) => { - Err(format!("<{}> is not a valid number of sides", $n)) +macro_rules! value_message { + ($n:ident, $t:expr) => { + Err(format!("<{}> is not a valid {}", $n, $t)) }; } @@ -59,7 +53,7 @@ fn main() -> Result<(), error::Error> { return Ok(()); } } - scale_message!(n) + value_message!(n,"scale value") }), ) .arg( @@ -128,6 +122,39 @@ fn main() -> Result<(), error::Error> { ) .subcommand(SubCommand::with_name("ngon") .about("Make an ngon") + .arg( + Arg::with_name("angle") + .short("a") + .long("angle") + .help("Angle in radians by which to rotate the ngon") + .takes_value(true) + .multiple(false) + .value_name("RAD") + .validator(|n: String| -> Result<(), String> { + if n.parse::().is_err() { + value_message!(n,"angle in radians") + }else{ + Ok(()) + } + }), + ) + .arg( + Arg::with_name("degrees") + .short("e") + .long("degrees") + .help("Angle in degrees by which to rotate the ngon") + .conflicts_with("angle") + .takes_value(true) + .multiple(false) + .value_name("DEG") + .validator(|n: String| -> Result<(), String> { + if n.parse::().is_err() { + value_message!(n,"angle in degrees") + }else{ + Ok(()) + } + }), + ) .arg( Arg::with_name("N") .help("The number of sides of the ngon") @@ -139,7 +166,7 @@ fn main() -> Result<(), error::Error> { return Ok(()); } } - sides_message!(n) + value_message!(n,"number of sides") }), ) .arg( @@ -240,12 +267,33 @@ fn main() -> Result<(), error::Error> { debug, )?; } else if let Some(submatches) = matches.subcommand_matches("ngon") { + let mut labels = HashMap::new(); + let angleident; + + if let Some(value) = submatches.value_of("angle") { + let angle_exp = astree::Expression::float(value.parse::().unwrap()); + labels.insert(String::from("@clangle"), angle_exp); + angleident = Some(String::from("@clangle")); + } else if let Some(value) = submatches.value_of("degrees") { + let angle_exp = astree::Expression::float(value.parse::().unwrap().to_radians()); + labels.insert(String::from("@clangle"), angle_exp); + angleident = Some(String::from("@clangle")); + } else { + angleident = None; + } + output_folder = submatches.value_of("OUTPUT_DIR").unwrap_or(output_folder); - structure = ngon::generate( + structure = ngon::generate::( submatches .value_of("N") .map(|n| n.parse::().unwrap()) .unwrap(), + None, + None, + None, + angleident, + None, + Some(labels), )?; } else { println!("{}", matches.usage()); diff --git a/src/ngon.rs b/src/ngon.rs index 57c5beb..1ed54fb 100644 --- a/src/ngon.rs +++ b/src/ngon.rs @@ -3,29 +3,49 @@ use crate::astree::{Boundaries, Boundary, Condition, Expression, FunctionType, J use crate::error; use std::collections::HashMap; -pub fn generate(n: u8) -> Result { +pub fn generate + Clone, S: Into + Copy>( + n: u8, + prefix_val: Option, + x_offset: Option, + y_offset: Option, + angle_offset: Option, + scale: Option, + previous_labels: Option>, +) -> Result { + let prefix = if let Some(val) = prefix_val { + val.into() + } else { + "".to_string() + }; + let mut junctions = Vec::::with_capacity(n as usize); - let mut labels = HashMap::::new(); + let mut labels = previous_labels.unwrap_or_default(); let mut vars = HashMap::::new(); vars.insert('s', 1_f64); let vars_eval = Some(&vars); let double_n: u16 = (n as u16) * 2; - let mut min_x = 1_f64; + let mut min_x = f64::MAX; let mut min_x_label = String::from("error"); - let mut max_x = -1_f64; + let mut max_x = f64::MIN; let mut max_x_label = String::from("error"); - let mut min_y = 1_f64; + let mut min_y = f64::MAX; let mut min_y_label = String::from("error"); - let mut max_y = -1_f64; + let mut max_y = f64::MIN; let mut max_y_label = String::from("error"); + let multiplier = if let Some(new_scale) = scale { + Expression::operation('*', Expression::var('s'), Expression::float(new_scale)) + } else { + Expression::var('s') + }; + for i in 0..n { - let angle_label = format!("@angle{}", i); - let angle_exp = Expression::operation( + let angle_label = format!("@{}angle{}", prefix, i); + let angle_inner = Expression::operation( '/', Expression::operation( '*', @@ -34,19 +54,32 @@ pub fn generate(n: u8) -> Result { ), Expression::float(n), ); + let angle_offset_clone = angle_offset.clone(); + let angle_exp = if let Some(ident) = angle_offset_clone { + Expression::operation('+', Expression::ident(ident.into()), angle_inner) + } else { + angle_inner + }; + labels.insert(angle_label.to_string(), angle_exp); let idents = Some(&labels); - let x_label = format!("@x{}", i); - let x_exp = Expression::operation( + let x_label = format!("@{}x{}", prefix, i); + let x_base = Expression::operation( '*', - Expression::var('s'), + multiplier.clone(), Expression::function( FunctionType::Cos, Expression::ident(angle_label.to_string()), ), ); + let x_offset_clone = x_offset.clone(); + let x_exp = if let Some(ident) = x_offset_clone { + Expression::operation('+', x_base, Expression::ident(ident.into())) + } else { + x_base + }; let x_val = x_exp.eval(&idents, &vars_eval)?; labels.insert(x_label.to_string(), x_exp); @@ -62,15 +95,21 @@ pub fn generate(n: u8) -> Result { let idents = Some(&labels); - let y_label = format!("@y{}", i); - let y_exp = Expression::operation( + let y_label = format!("@{}y{}", prefix, i); + let y_base = Expression::operation( '*', - Expression::var('s'), + multiplier.clone(), Expression::function( FunctionType::Sin, Expression::ident(angle_label.to_string()), ), ); + let y_offset_clone = y_offset.clone(); + let y_exp = if let Some(ident) = y_offset_clone { + Expression::operation('+', y_base, Expression::ident(ident.into())) + } else { + y_base + }; let y_val = y_exp.eval(&idents, &vars_eval)?; labels.insert(y_label.to_string(), y_exp); @@ -86,8 +125,8 @@ pub fn generate(n: u8) -> Result { let normal_index: u16 = (i as u16) * 2 + 1; - let normal_angle_label = format!("@nangle{}", i); - let normal_angle_exp = Expression::operation( + let normal_angle_label = format!("@{}nangle{}", prefix, i); + let normal_angle_base = Expression::operation( '/', Expression::operation( '*', @@ -96,16 +135,22 @@ pub fn generate(n: u8) -> Result { ), Expression::float(double_n), ); + let angle_offset_clone = angle_offset.clone(); + let normal_angle_exp = if let Some(ident) = angle_offset_clone { + Expression::operation('+', normal_angle_base, Expression::ident(ident.into())) + } else { + normal_angle_base + }; labels.insert(normal_angle_label.to_string(), normal_angle_exp); - let normal_x_label = format!("@nx{}", i); + let normal_x_label = format!("@{}nx{}", prefix, i); let normal_x_exp = Expression::function( FunctionType::Cos, Expression::ident(normal_angle_label.to_string()), ); labels.insert(normal_x_label.to_string(), normal_x_exp); - let normal_y_label = format!("@ny{}", i); + let normal_y_label = format!("@{}ny{}", prefix, i); let normal_y_exp = Expression::function( FunctionType::Sin, Expression::ident(normal_angle_label.to_string()),