From 4c0c8b7c68e76e42fbdf3f4f08898177ce25f831 Mon Sep 17 00:00:00 2001 From: raffitz Date: Tue, 27 Jul 2021 14:41:25 +0100 Subject: [PATCH] Add remake flag --- .gitignore | 1 + Cargo.lock | 2 +- Cargo.toml | 2 +- src/astree.rs | 147 ++++++++++++++++++++++++-- src/main.rs | 63 ++++++++++- src/ngon.rs | 6 +- src/star.rs | 120 +++++++++++++++++++++ src/triangle.rs | 274 ++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 597 insertions(+), 18 deletions(-) create mode 100644 src/star.rs create mode 100644 src/triangle.rs diff --git a/.gitignore b/.gitignore index ecc1ed7..91cc3a9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ */*.csv */*.txt */*.gv +*/remake.solid *.gv diff --git a/Cargo.lock b/Cargo.lock index 9ce3fe2..cf2a989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -311,7 +311,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "voxelmap" -version = "0.5.0" +version = "0.6.0" dependencies = [ "clap", "lodepng", diff --git a/Cargo.toml b/Cargo.toml index 355b069..3b8eb4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "voxelmap" -version = "0.5.0" +version = "0.6.0" authors = ["raffitz "] edition = "2018" description = "Creates voxel maps to build voxelised objects" diff --git a/src/astree.rs b/src/astree.rs index f5ef4f6..56a8fe5 100644 --- a/src/astree.rs +++ b/src/astree.rs @@ -1,5 +1,6 @@ use crate::error::Error; use std::collections::{HashMap, HashSet}; +use std::fmt; use std::io::Write; #[derive(Debug, PartialEq, Clone)] @@ -22,12 +23,42 @@ pub enum FunctionType { Neg, } +impl fmt::Display for FunctionType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let text = match *self { + FunctionType::Sin => "sin", + FunctionType::Cos => "cos", + FunctionType::Tan => "tan", + FunctionType::Sec => "sec", + FunctionType::Csc => "csc", + FunctionType::Cot => "cot", + FunctionType::Asin => "asin", + FunctionType::Acos => "acos", + FunctionType::Atan => "atan", + FunctionType::Sign => "sign", + FunctionType::Abs => "abs", + FunctionType::Sqrt => "√", + FunctionType::Exp => "exp", + FunctionType::Ln => "ln", + FunctionType::Log => "log", + FunctionType::Neg => "-", + }; + text.fmt(f) + } +} + #[derive(Debug, Clone)] pub struct FunctionData { kind: FunctionType, arg: Box, } +impl fmt::Display for FunctionData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}({})", self.kind, self.arg) + } +} + impl FunctionData { pub fn new(kind: FunctionType, arg_exp: Expression) -> Self { let arg = Box::new(arg_exp); @@ -98,6 +129,12 @@ pub struct OperationData { right: Box, } +impl fmt::Display for OperationData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({} {} {})", self.left, self.kind, self.right) + } +} + impl OperationData { pub fn new(kind: char, left_exp: Expression, right_exp: Expression) -> Self { let left = Box::new(left_exp); @@ -151,6 +188,18 @@ pub enum Expression { Operation(OperationData), } +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Expression::Var(v) => v.fmt(f), + Expression::Float(n) => n.fmt(f), + Expression::Ident(s) => s.fmt(f), + Expression::Function(function) => function.fmt(f), + Expression::Operation(op) => op.fmt(f), + } + } +} + impl Expression { pub fn var(c: char) -> Self { Expression::Var(c) @@ -254,34 +303,38 @@ impl Expression { pub fn ident_dependencies( &self, idents: &Option<&HashMap>, - ) -> Result, Error> { + ) -> Result, Error> { match self { Expression::Float(_) => Ok(HashSet::new()), Expression::Function(f) => f.arg.ident_dependencies(idents), Expression::Operation(o) => { let left = o.left.ident_dependencies(idents)?; let right = o.right.ident_dependencies(idents)?; - let mut result: HashSet = HashSet::new(); - for dep in left { - result.insert(dep); - } - for dep in right { - result.insert(dep); + let mut result: HashSet<(String, usize)> = HashSet::new(); + for dep in left.union(&right) { + result.insert(dep.to_owned()); } Ok(result) } Expression::Var(_) => Ok(HashSet::new()), Expression::Ident(s) => { - let mut result: HashSet = HashSet::new(); - result.insert(s.to_owned()); + let mut result: HashSet<(String, usize)> = HashSet::new(); + + let mut max_recursion: usize = 0; + let ident_exps = idents.as_ref().ok_or::(Error::MissingIdentMap)?; let referred = ident_exps .get(s) .ok_or::(Error::MissingIdentAssignment)?; let recursive = referred.ident_dependencies(idents)?; for dep in recursive { + let (_, level) = dep; result.insert(dep); + if level + 1 > max_recursion { + max_recursion = level + 1; + } } + result.insert((s.to_owned(), max_recursion)); Ok(result) } } @@ -295,6 +348,12 @@ pub struct Condition { right: Box, } +impl fmt::Display for Condition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.left, self.kind, self.right) + } +} + impl Condition { pub fn new(kind: char, left_exp: Expression, right_exp: Expression) -> Self { let left = Box::new(left_exp); @@ -336,6 +395,19 @@ impl Condition { )?; Ok(max_id) } + + pub fn ident_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + let left_idents = self.left.ident_dependencies(idents)?; + let right_idents = self.right.ident_dependencies(idents)?; + let mut result: HashSet<(String, usize)> = HashSet::new(); + for dep in left_idents.union(&right_idents) { + result.insert(dep.to_owned()); + } + Ok(result) + } } #[derive(Debug)] @@ -345,6 +417,12 @@ pub struct JunctionData { right: Box, } +impl fmt::Display for JunctionData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{{{} {} {}}}", self.left, self.kind, self.right) + } +} + impl JunctionData { pub fn new(kind: char, left_cond: Junction, right_cond: Junction) -> Self { let left = Box::new(left_cond); @@ -386,6 +464,19 @@ impl JunctionData { )?; Ok(max_id) } + + pub fn ident_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + let left_idents = self.left.ident_dependencies(idents)?; + let right_idents = self.right.ident_dependencies(idents)?; + let mut result: HashSet<(String, usize)> = HashSet::new(); + for dep in left_idents.union(&right_idents) { + result.insert(dep.to_owned()); + } + Ok(result) + } } #[derive(Debug)] @@ -394,6 +485,15 @@ pub enum Junction { Meta(JunctionData), } +impl fmt::Display for Junction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Junction::Singleton(condition) => condition.fmt(f), + Junction::Meta(meta) => meta.fmt(f), + } + } +} + impl Junction { pub fn singleton(cond: Condition) -> Self { Junction::Singleton(cond) @@ -421,6 +521,16 @@ impl Junction { Junction::Singleton(cond) => cond.graph(output, id), } } + + pub fn ident_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + match self { + Junction::Singleton(cond) => cond.ident_dependencies(idents), + Junction::Meta(meta) => meta.ident_dependencies(idents), + } + } } #[derive(Debug)] @@ -430,6 +540,12 @@ pub struct Boundary { pub max: Expression, } +impl fmt::Display for Boundary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} ≤ {} ≤ {}", self.min, self.var, self.max) + } +} + impl Boundary { pub fn new( l: Expression, @@ -458,6 +574,19 @@ impl Boundary { } Err(()) } + + pub fn ident_dependencies( + &self, + idents: &Option<&HashMap>, + ) -> Result, Error> { + let left_idents = self.min.ident_dependencies(idents)?; + let right_idents = self.max.ident_dependencies(idents)?; + let mut result: HashSet<(String, usize)> = HashSet::new(); + for dep in left_idents.union(&right_idents) { + result.insert(dep.to_owned()); + } + Ok(result) + } } pub type Boundaries = [Boundary; 3]; diff --git a/src/main.rs b/src/main.rs index e937e9f..9b7bb16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,6 +114,14 @@ fn main() -> Result<(), error::Error> { .takes_value(false) .multiple(false), ) + .arg( + Arg::with_name("remake") + .short("r") + .long("remake") + .help("Remake definition file from internal state") + .takes_value(false) + .multiple(false), + ) .arg( Arg::with_name("test") .short("t") @@ -320,6 +328,7 @@ fn main() -> Result<(), error::Error> { } let graph = matches.is_present("graph"); + let remake = matches.is_present("remake"); let test = matches.is_present("test"); let mut output_folder = "."; @@ -371,8 +380,7 @@ fn main() -> Result<(), error::Error> { .map(|n| n.parse::().unwrap()) .unwrap(), None, - None, - None, + (None, None), angleident, None, Some(labels), @@ -393,7 +401,9 @@ fn main() -> Result<(), error::Error> { angleident = None; } - let star_factor = submatches.value_of("factor").map(|f| f.parse::().unwrap()); + let star_factor = submatches + .value_of("factor") + .map(|f| f.parse::().unwrap()); output_folder = submatches.value_of("OUTPUT_DIR").unwrap_or(output_folder); structure = star::generate::( @@ -402,8 +412,7 @@ fn main() -> Result<(), error::Error> { .map(|n| n.parse::().unwrap()) .unwrap(), None, - None, - None, + (None, None), angleident, None, star_factor, @@ -515,6 +524,50 @@ fn main() -> Result<(), error::Error> { writeln!(gv_file, "}}")?; } + // Print remake + if remake { + let mut remake_file = fs::File::create(format! {"{}/remake.solid",output_folder})?; + writeln!( + remake_file, + "# Solid specifications remade by {} v{}, by {} */\n\n", + crate_name!(), + crate_version!(), + crate_authors!() + )?; + + let lookup = Some(&idents); + + // Print tree and boundary dependencies + let mut complete_deps = tree.ident_dependencies(&lookup)?; + for limit in &limits { + let limit_deps = limit.ident_dependencies(&lookup)?; + for dep in limit_deps { + complete_deps.insert(dep); + } + } + let mut vector_deps: Vec<(String, usize)> = complete_deps.into_iter().collect(); + + vector_deps.sort_unstable_by(|(_, i), (_, j)| i.cmp(j)); + + for dep in vector_deps { + let (name, _) = dep; + let definition = idents + .get(&name) + .ok_or::(Error::MissingIdentAssignment)?; + writeln!(remake_file, "define {} {}", name, definition)?; + } + writeln!(remake_file, "\n")?; + + // Print limits + for limit in &limits { + writeln!(remake_file, "{}", limit)?; + } + writeln!(remake_file, "\n")?; + + // Print tree + writeln!(remake_file, "{}", tree)?; + } + if test { return Ok(()); } diff --git a/src/ngon.rs b/src/ngon.rs index 1ed54fb..deefb86 100644 --- a/src/ngon.rs +++ b/src/ngon.rs @@ -6,8 +6,7 @@ use std::collections::HashMap; pub fn generate + Clone, S: Into + Copy>( n: u8, prefix_val: Option, - x_offset: Option, - y_offset: Option, + offset: (Option, Option), angle_offset: Option, scale: Option, previous_labels: Option>, @@ -27,6 +26,8 @@ pub fn generate + Clone, S: Into + Copy>( let double_n: u16 = (n as u16) * 2; + let (x_offset, y_offset) = offset; + let mut min_x = f64::MAX; let mut min_x_label = String::from("error"); let mut max_x = f64::MIN; @@ -74,6 +75,7 @@ pub fn generate + Clone, S: Into + Copy>( 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())) diff --git a/src/star.rs b/src/star.rs new file mode 100644 index 0000000..06e60de --- /dev/null +++ b/src/star.rs @@ -0,0 +1,120 @@ +use crate::astree; +use crate::astree::{Expression, Junction}; +use crate::error; +use crate::ngon; +use crate::triangle::any_triangle; +use std::collections::HashMap; + +pub fn generate + Clone, S: Into + Copy>( + n: u8, + prefix_val: Option, + offset: (Option, Option), + angle_offset: Option, + scale: Option, + star_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 = previous_labels.unwrap_or_default(); + + let (x_offset, y_offset) = offset; + + let center_x; + let center_y; + if let Some(x_label) = x_offset { + center_x = x_label.into(); + } else { + center_x = format!("@{}centerx", prefix); + labels.insert(center_x.clone(), Expression::float(0_f64)); + } + if let Some(y_label) = y_offset { + center_y = y_label.into(); + } else { + center_y = format!("@{}centery", prefix); + labels.insert(center_y.clone(), Expression::float(0_f64)); + } + + let new_angle_offset = angle_offset.map(|a| a.into()); + let new_scale = scale.map(|s| s.into()); + + let inner_prefix = format!("{}inner", prefix); + + let (new_labels, _, _) = ngon::generate( + n, + Some(inner_prefix.clone()), + (Some(center_x.clone()), Some(center_y.clone())), + new_angle_offset, + new_scale, + Some(labels), + )?; + + let angle_offset_label = format!("@{}innernangle0", prefix); + let outer_prefix = format!("{}outer", prefix); + + let outer_scale = star_scale.map(|s| s.into()).unwrap_or(2_f64) * new_scale.unwrap_or(1_f64); + + let (definitive_labels, boundaries, _) = ngon::generate( + n, + Some(outer_prefix.clone()), + (Some(center_x.clone()), Some(center_y.clone())), + Some(angle_offset_label), + Some(outer_scale), + new_labels, + )?; + + let mut input_labels = definitive_labels; + + for i in 0..n { + let label_x_1 = format!("@{}x{}", inner_prefix, i); + let label_y_1 = format!("@{}y{}", inner_prefix, i); + let label_x_2 = format!("@{}x{}", outer_prefix, i); + let label_y_2 = format!("@{}y{}", outer_prefix, i); + let (definitive_labels, _, junction) = any_triangle( + center_x.clone(), + center_y.clone(), + label_x_1, + label_y_1, + label_x_2, + label_y_2, + input_labels, + )?; + + input_labels = definitive_labels; + + junctions.push(junction); + + let label_x_1 = format!("@{}x{}", outer_prefix, i); + let label_y_1 = format!("@{}y{}", outer_prefix, i); + let label_x_2 = format!("@{}x{}", inner_prefix, (i + 1) % n); + let label_y_2 = format!("@{}y{}", inner_prefix, (i + 1) % n); + let (definitive_labels, _, junction) = any_triangle( + center_x.clone(), + center_y.clone(), + label_x_1, + label_y_1, + label_x_2, + label_y_2, + input_labels, + )?; + + input_labels = definitive_labels; + + junctions.push(junction); + } + + while junctions.len() > 1 { + let left = junctions.remove(0); + let right = junctions.remove(0); + junctions.push(Junction::meta('⋁', left, right)); + } + let the_junction = junctions.pop().unwrap(); + + Ok((input_labels, boundaries, the_junction)) +} diff --git a/src/triangle.rs b/src/triangle.rs new file mode 100644 index 0000000..b113397 --- /dev/null +++ b/src/triangle.rs @@ -0,0 +1,274 @@ +use crate::astree; +use crate::astree::{Boundaries, Boundary, Condition, Expression, Junction}; +use crate::error; +use std::collections::HashMap; + +pub fn any_triangle + Clone>( + x_1: T, + y_1: T, + x_2: T, + y_2: T, + x_3: T, + y_3: T, + previous_labels: Option>, +) -> Result { + let mut junctions = Vec::::with_capacity(3); + + let labels = previous_labels.unwrap_or_default(); + let mut vars = HashMap::::new(); + vars.insert('s', 1_f64); + let vars_eval = Some(&vars); + + // Determine if clockwise + let idents = Some(&labels); + let clockwise_exp = Expression::operation( + '+', + Expression::operation( + '+', + Expression::operation( + '*', + Expression::operation( + '-', + Expression::ident(x_3.clone().into()), + Expression::ident(x_2.clone().into()), + ), + Expression::operation( + '+', + Expression::ident(y_3.clone().into()), + Expression::ident(y_2.clone().into()), + ), + ), + Expression::operation( + '*', + Expression::operation( + '-', + Expression::ident(x_1.clone().into()), + Expression::ident(x_3.clone().into()), + ), + Expression::operation( + '+', + Expression::ident(y_1.clone().into()), + Expression::ident(y_3.clone().into()), + ), + ), + ), + Expression::operation( + '*', + Expression::operation( + '-', + Expression::ident(x_2.clone().into()), + Expression::ident(x_1.clone().into()), + ), + Expression::operation( + '+', + Expression::ident(y_2.clone().into()), + Expression::ident(y_1.clone().into()), + ), + ), + ); + let clockwise = clockwise_exp.eval(&idents, &vars_eval)? > 1e-10_f64; + + let ident_x_1 = x_1.into(); + let ident_y_1 = y_1.into(); + let ident_x_2; + let ident_y_2; + let ident_x_3; + let ident_y_3; + if clockwise { + ident_x_2 = x_3.into(); + ident_y_2 = y_3.into(); + ident_x_3 = x_2.into(); + ident_y_3 = y_2.into(); + } else { + ident_x_2 = x_2.into(); + ident_y_2 = y_2.into(); + ident_x_3 = x_3.into(); + ident_y_3 = y_3.into(); + } + + let mut min_x = f64::MAX; + let mut min_x_label = String::from("error"); + let mut max_x = f64::MIN; + let mut max_x_label = String::from("error"); + + let mut min_y = f64::MAX; + let mut min_y_label = String::from("error"); + let mut max_y = f64::MIN; + let mut max_y_label = String::from("error"); + + let x_1_val = Expression::ident(ident_x_1.clone()).eval(&idents, &vars_eval)?; + let x_2_val = Expression::ident(ident_x_2.clone()).eval(&idents, &vars_eval)?; + let x_3_val = Expression::ident(ident_x_3.clone()).eval(&idents, &vars_eval)?; + let y_1_val = Expression::ident(ident_y_1.clone()).eval(&idents, &vars_eval)?; + let y_2_val = Expression::ident(ident_y_2.clone()).eval(&idents, &vars_eval)?; + let y_3_val = Expression::ident(ident_y_3.clone()).eval(&idents, &vars_eval)?; + + if x_1_val < min_x { + min_x = x_1_val; + min_x_label = ident_x_1.clone(); + } + if x_2_val < min_x { + min_x = x_2_val; + min_x_label = ident_x_2.clone(); + } + if x_3_val < min_x { + min_x_label = ident_x_3.clone(); + } + if x_1_val > max_x { + max_x = x_1_val; + max_x_label = ident_x_1.clone(); + } + if x_2_val > max_x { + max_x = x_2_val; + max_x_label = ident_x_2.clone(); + } + if x_3_val > max_x { + max_x_label = ident_x_3.clone(); + } + if y_1_val < min_y { + min_y = y_1_val; + min_y_label = ident_y_1.clone(); + } + if y_2_val < min_y { + min_y = y_2_val; + min_y_label = ident_y_2.clone(); + } + if y_3_val < min_y { + min_y_label = ident_y_3.clone(); + } + if y_1_val > max_y { + max_y = y_1_val; + max_y_label = ident_y_1.clone(); + } + if y_2_val > max_y { + max_y = y_2_val; + max_y_label = ident_y_2.clone(); + } + if y_3_val > max_y { + max_y_label = ident_y_3.clone(); + } + + let normal_1_y = Expression::operation( + '-', + Expression::ident(ident_x_1.clone()), + Expression::ident(ident_x_2.clone()), + ) + .eval(&idents, &vars_eval)?; + let normal_1_x = Expression::operation( + '-', + Expression::ident(ident_y_2.clone()), + Expression::ident(ident_y_1.clone()), + ) + .eval(&idents, &vars_eval)?; + let normal_2_y = Expression::operation( + '-', + Expression::ident(ident_x_2.clone()), + Expression::ident(ident_x_3.clone()), + ) + .eval(&idents, &vars_eval)?; + let normal_2_x = Expression::operation( + '-', + Expression::ident(ident_y_3.clone()), + Expression::ident(ident_y_2.clone()), + ) + .eval(&idents, &vars_eval)?; + let normal_3_y = Expression::operation( + '-', + Expression::ident(ident_x_3.clone()), + Expression::ident(ident_x_1.clone()), + ) + .eval(&idents, &vars_eval)?; + let normal_3_x = Expression::operation( + '-', + Expression::ident(ident_y_1.clone()), + Expression::ident(ident_y_3.clone()), + ) + .eval(&idents, &vars_eval)?; + + let j_exp = Expression::operation( + '+', + Expression::operation( + '*', + Expression::float(normal_1_x), + Expression::operation('-', Expression::var('x'), Expression::ident(ident_x_1)), + ), + Expression::operation( + '*', + Expression::float(normal_1_y), + Expression::operation('-', Expression::var('y'), Expression::ident(ident_y_1)), + ), + ); + + junctions.push(Junction::singleton(Condition::new( + '≤', + j_exp, + Expression::float(0), + ))); + + let j_exp = Expression::operation( + '+', + Expression::operation( + '*', + Expression::float(normal_2_x), + Expression::operation('-', Expression::var('x'), Expression::ident(ident_x_2)), + ), + Expression::operation( + '*', + Expression::float(normal_2_y), + Expression::operation('-', Expression::var('y'), Expression::ident(ident_y_2)), + ), + ); + + junctions.push(Junction::singleton(Condition::new( + '≤', + j_exp, + Expression::float(0), + ))); + + let j_exp = Expression::operation( + '+', + Expression::operation( + '*', + Expression::float(normal_3_x), + Expression::operation('-', Expression::var('x'), Expression::ident(ident_x_3)), + ), + Expression::operation( + '*', + Expression::float(normal_3_y), + Expression::operation('-', Expression::var('y'), Expression::ident(ident_y_3)), + ), + ); + + junctions.push(Junction::singleton(Condition::new( + '≤', + j_exp, + Expression::float(0), + ))); + + while junctions.len() > 1 { + let left = junctions.remove(0); + let right = junctions.remove(0); + junctions.push(Junction::meta('⋀', left, right)); + } + let the_junction = junctions.pop().unwrap(); + + let boundaries: Boundaries = [ + Boundary::new(Expression::float(0), '≤', 'z', '≤', Expression::float(0))?, + Boundary::new( + Expression::ident(min_x_label), + '≤', + 'x', + '≤', + Expression::ident(max_x_label), + )?, + Boundary::new( + Expression::ident(min_y_label), + '≤', + 'y', + '≤', + Expression::ident(max_y_label), + )?, + ]; + + Ok((Some(labels), boundaries, the_junction)) +}