Browse Source

Add ngon subcommand

main
raffitz 3 years ago
parent
commit
6ac6569284
Signed by: raffitz
GPG Key ID: BB3596BD0A31252D
  1. 2
      Cargo.lock
  2. 4
      Cargo.toml
  3. 45
      src/astree.rs
  4. 1
      src/error.rs
  5. 116
      src/main.rs
  6. 170
      src/ngon.rs
  7. 50
      src/parser.rs

2
Cargo.lock generated

@ -311,7 +311,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" @@ -311,7 +311,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "voxelmap"
version = "0.3.3"
version = "0.4.0"
dependencies = [
"clap",
"lodepng",

4
Cargo.toml

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
[package]
name = "voxelmap"
version = "0.3.3"
version = "0.4.0"
authors = ["raffitz <raf.a.m.c.gon@gmail.com>"]
edition = "2018"
description = "Converts mathematical descriptions of objects to voxel maps"
description = "Creates voxel maps to build voxelised objects"
[dependencies]
clap = "2.33"

45
src/astree.rs

@ -156,8 +156,8 @@ impl Expression { @@ -156,8 +156,8 @@ impl Expression {
Expression::Var(c)
}
pub fn float(f: f64) -> Self {
Expression::Float(f)
pub fn float<T: Into<f64>>(f: T) -> Self {
Expression::Float(f.into())
}
pub fn ident(s: String) -> Self {
@ -422,3 +422,44 @@ impl Junction { @@ -422,3 +422,44 @@ impl Junction {
}
}
}
#[derive(Debug)]
pub struct Boundary {
pub var: char,
pub min: Expression,
pub max: Expression,
}
impl Boundary {
pub fn new(
l: Expression,
lcond: char,
var: char,
rcond: char,
r: Expression,
) -> Result<Self, ()> {
if var != 'x' && var != 'y' && var != 'z' {
return Err(());
}
if lcond == '<' || lcond == '≤' {
if rcond == '<' || rcond == '≤' {
let min = l;
let max = r;
return Ok(Boundary { var, min, max });
}
return Err(());
} else if lcond == '>' || lcond == '≥' {
if rcond == '>' || rcond == '≥' {
let min = r;
let max = l;
return Ok(Boundary { var, min, max });
}
return Err(());
}
Err(())
}
}
pub type Boundaries = [Boundary; 3];
pub type Structure = (Option<HashMap<String, Expression>>, Boundaries, Junction);

1
src/error.rs

@ -11,6 +11,7 @@ pub enum Error { @@ -11,6 +11,7 @@ pub enum Error {
MissingIdentAssignment,
MissingVarMap,
MissingIdentMap,
MissingSubCommand,
IllegalVarInBoundary,
IllegarBoundedVar,
UnboundedVar,

116
src/main.rs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg, SubCommand};
use nbt::encode::write_gzip_compound_tag;
use nbt::{CompoundTag, Tag};
use rgb::*;
@ -8,6 +8,7 @@ use std::io::{Error, ErrorKind, Read, Write}; @@ -8,6 +8,7 @@ use std::io::{Error, ErrorKind, Read, Write};
mod astree;
mod error;
mod ngon;
mod parser;
macro_rules! scale_message {
@ -16,6 +17,12 @@ macro_rules! scale_message { @@ -16,6 +17,12 @@ macro_rules! scale_message {
};
}
macro_rules! sides_message {
($n:ident) => {
Err(format!("<{}> is not a valid number of sides", $n))
};
}
macro_rules! csv_line {
($f:ident, $n:expr, $t:expr, $c:expr, $s:expr, $b:expr) => {
writeln!($f, "{},{},{},{},{}", $n, $t, $c, $s, $b)?;
@ -45,7 +52,7 @@ fn main() -> Result<(), error::Error> { @@ -45,7 +52,7 @@ fn main() -> Result<(), error::Error> {
.help("The scale parameter for the object")
.takes_value(true)
.multiple(false)
.value_name("N")
.value_name("S")
.validator(|n: String| -> Result<(), String> {
if let Ok(scale) = n.parse::<f64>() {
if scale >= 0_f64 {
@ -99,7 +106,7 @@ fn main() -> Result<(), error::Error> { @@ -99,7 +106,7 @@ fn main() -> Result<(), error::Error> {
Arg::with_name("debug")
.short("d")
.long("debug")
.help("Show parsing steps")
.help("Show intermediate steps")
.takes_value(false)
.multiple(false),
)
@ -115,12 +122,44 @@ fn main() -> Result<(), error::Error> { @@ -115,12 +122,44 @@ fn main() -> Result<(), error::Error> {
Arg::with_name("test")
.short("t")
.long("test")
.help("Parses the input file, does not output")
.help("Parses the input, does not output")
.takes_value(false)
.multiple(false),
)
.arg(
Arg::with_name("FILE")
.subcommand(SubCommand::with_name("ngon")
.about("Make an ngon")
.arg(
Arg::with_name("N")
.help("The number of sides of the ngon")
.required(true)
.index(1)
.validator(|n: String| -> Result<(), String> {
if let Ok(sides) = n.parse::<u8>() {
if (3..=100).contains(&sides) {
return Ok(());
}
}
sides_message!(n)
}),
)
.arg(
Arg::with_name("OUTPUT_DIR")
.help("The folder where the output images will be stored")
.required(true)
.index(2)
.validator(move |path: String| -> Result<(), String> {
match fs::create_dir(&path) {
Ok(_) => Ok(()),
Err(error) => Err(io_error(error, &path)),
}
})
.conflicts_with("test"),
)
)
.subcommand(SubCommand::with_name("solid")
.about("Read mathematical description of solid")
.arg(
Arg::with_name("FILE")
.help("The file describing the shape to map")
.required(true)
.index(1)
@ -129,10 +168,10 @@ fn main() -> Result<(), error::Error> { @@ -129,10 +168,10 @@ fn main() -> Result<(), error::Error> {
Ok(_) => Ok(()),
Err(error) => Err(io_error(error, &path)),
}
}),
)
.arg(
Arg::with_name("OUTPUT_DIR")
})
)
.arg(
Arg::with_name("OUTPUT_DIR")
.help("The folder where the output images will be stored")
.required(true)
.index(2)
@ -143,13 +182,12 @@ fn main() -> Result<(), error::Error> { @@ -143,13 +182,12 @@ fn main() -> Result<(), error::Error> {
}
})
.conflicts_with("test"),
)
)
.get_matches();
let scale = matches.value_of("scale").map(|s| s.parse::<f64>().unwrap());
let mut object_description = fs::File::open(matches.value_of("FILE").unwrap()).unwrap();
let debug = matches.is_present("debug");
let offset = matches.is_present("offset");
@ -175,30 +213,46 @@ fn main() -> Result<(), error::Error> { @@ -175,30 +213,46 @@ fn main() -> Result<(), error::Error> {
let graph = matches.is_present("graph");
let test = matches.is_present("test");
let output_folder = if test {
"."
} else {
matches.value_of("OUTPUT_DIR").unwrap()
};
let mut output_folder = ".";
let mut data = String::new();
let structure: astree::Structure;
let read_count = object_description.read_to_string(&mut data)?;
if let Some(submatches) = matches.subcommand_matches("solid") {
let mut data = String::new();
if debug {
println!(
"\nRead {} bytes, scale is {}",
read_count,
scale.unwrap_or(1.0_f64)
);
let mut object_description = fs::File::open(submatches.value_of("FILE").unwrap()).unwrap();
output_folder = submatches.value_of("OUTPUT_DIR").unwrap_or(output_folder);
let read_count = object_description.read_to_string(&mut data)?;
if debug {
println!(
"\nRead {} bytes, scale is {}",
read_count,
scale.unwrap_or(1.0_f64)
);
}
structure = parser::parse(
&data,
submatches.value_of("FILE").unwrap(),
submatches.value_of("OUTPUT_DIR"),
debug,
)?;
} else if let Some(submatches) = matches.subcommand_matches("ngon") {
output_folder = submatches.value_of("OUTPUT_DIR").unwrap_or(output_folder);
structure = ngon::generate(
submatches
.value_of("N")
.map(|n| n.parse::<u8>().unwrap())
.unwrap(),
)?;
} else {
println!("{}", matches.usage());
return Err(Error::MissingSubCommand);
}
let (assigns, limits, tree) = parser::parse(
&data,
matches.value_of("FILE").unwrap(),
matches.value_of("OUTPUT_DIR"),
debug,
)?;
let (assigns, limits, tree) = structure;
let idents = assigns.unwrap_or_default();

170
src/ngon.rs

@ -0,0 +1,170 @@ @@ -0,0 +1,170 @@
use crate::astree;
use crate::astree::{Boundaries, Boundary, Condition, Expression, FunctionType, Junction};
use crate::error;
use std::collections::HashMap;
pub fn generate(n: u8) -> Result<astree::Structure, error::Error> {
let mut junctions = Vec::<Junction>::with_capacity(n as usize);
let mut labels = HashMap::<String, Expression>::new();
let mut vars = HashMap::<char, f64>::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_label = String::from("error");
let mut max_x = -1_f64;
let mut max_x_label = String::from("error");
let mut min_y = 1_f64;
let mut min_y_label = String::from("error");
let mut max_y = -1_f64;
let mut max_y_label = String::from("error");
for i in 0..n {
let angle_label = format!("@angle{}", i);
let angle_exp = Expression::operation(
'/',
Expression::operation(
'*',
Expression::float(i),
Expression::float(std::f64::consts::TAU),
),
Expression::float(n),
);
labels.insert(angle_label.to_string(), angle_exp);
let idents = Some(&labels);
let x_label = format!("@x{}", i);
let x_exp = Expression::operation(
'*',
Expression::var('s'),
Expression::function(
FunctionType::Cos,
Expression::ident(angle_label.to_string()),
),
);
let x_val = x_exp.eval(&idents, &vars_eval)?;
labels.insert(x_label.to_string(), x_exp);
if x_val < min_x {
min_x = x_val;
min_x_label = x_label.to_string();
}
if x_val > max_x {
max_x = x_val;
max_x_label = x_label.to_string();
}
let idents = Some(&labels);
let y_label = format!("@y{}", i);
let y_exp = Expression::operation(
'*',
Expression::var('s'),
Expression::function(
FunctionType::Sin,
Expression::ident(angle_label.to_string()),
),
);
let y_val = y_exp.eval(&idents, &vars_eval)?;
labels.insert(y_label.to_string(), y_exp);
if y_val < min_y {
min_y = y_val;
min_y_label = y_label.to_string();
}
if y_val > max_y {
max_y = y_val;
max_y_label = y_label.to_string();
}
let normal_index: u16 = (i as u16) * 2 + 1;
let normal_angle_label = format!("@nangle{}", i);
let normal_angle_exp = Expression::operation(
'/',
Expression::operation(
'*',
Expression::float(normal_index),
Expression::float(std::f64::consts::TAU),
),
Expression::float(double_n),
);
labels.insert(normal_angle_label.to_string(), normal_angle_exp);
let normal_x_label = format!("@nx{}", 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_exp = Expression::function(
FunctionType::Sin,
Expression::ident(normal_angle_label.to_string()),
);
labels.insert(normal_y_label.to_string(), normal_y_exp);
let j_exp = Expression::operation(
'+',
Expression::operation(
'*',
Expression::ident(normal_x_label.to_string()),
Expression::operation(
'-',
Expression::var('x'),
Expression::ident(x_label.to_string()),
),
),
Expression::operation(
'*',
Expression::ident(normal_y_label.to_string()),
Expression::operation(
'-',
Expression::var('y'),
Expression::ident(y_label.to_string()),
),
),
);
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))
}

50
src/parser.rs

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
use crate::astree;
use crate::error;
use crate::error::Error;
use logos::Logos;
@ -8,50 +9,9 @@ use std::path::Path; @@ -8,50 +9,9 @@ use std::path::Path;
pomelo! {
%include {
use logos::{Lexer, Logos};
use crate::astree::{Condition, Expression, FunctionType, Junction};
use crate::astree::{Boundary, Boundaries, Condition, Expression, FunctionType, Junction, Structure};
use std::collections::HashMap;
#[derive(Debug)]
pub struct Boundary{
pub var: char,
pub min: Expression,
pub max: Expression,
}
impl Boundary{
pub fn new(
l: Expression,
lcond: char,
var: char,
rcond: char,
r: Expression,
) -> Result<Self,()> {
if var != 'x' && var != 'y' && var != 'z' {
return Err(());
}
if lcond == '<' || lcond == '≤' {
if rcond == '<' || rcond == '≤'{
let min = l;
let max = r;
return Ok(Boundary{var,min,max});
}
return Err(());
}else if lcond == '>' || lcond == '≥'{
if rcond == '>' || rcond == '≥'{
let min = r;
let max = l;
return Ok(Boundary{var,min,max});
}
return Err(());
}
Err(())
}
}
pub type Boundaries = [Boundary; 3];
pub type Return = (Option<HashMap<String,Expression>>, Boundaries, Junction);
fn read_var(lex: &mut Lexer<Token>) -> Option<char> {
lex.slice().chars().next()
}
@ -162,7 +122,7 @@ pomelo! { @@ -162,7 +122,7 @@ pomelo! {
%right Function;
%left LineEnd;
%type input Return;
%type input Structure;
input ::= boundaries(L) metajuncture(J) { (None,L,J) }
input ::= LineEnd boundaries(L) metajuncture(J) { (None,L,J) }
input ::= assignments(A) boundaries(L) metajuncture(J) { (Some(A),L,J) }
@ -231,7 +191,7 @@ pub fn parse<P: AsRef<Path> + std::fmt::Display>( @@ -231,7 +191,7 @@ pub fn parse<P: AsRef<Path> + std::fmt::Display>(
file: P,
output_dir: Option<P>,
debug: bool,
) -> Result<parser::Return, error::Error> {
) -> Result<astree::Structure, error::Error> {
let lex = parser::Token::lexer(source);
let mut p = parser::Parser::new();
@ -285,7 +245,7 @@ pub fn parse<P: AsRef<Path> + std::fmt::Display>( @@ -285,7 +245,7 @@ pub fn parse<P: AsRef<Path> + std::fmt::Display>(
Ok(result) => Ok(result),
Err(_) => {
eprintln!("{}: Unexpected end of file", file);
if let Some(the_dir) = output_dir{
if let Some(the_dir) = output_dir {
fs::remove_dir(the_dir)?;
}
Err(Error::ParserError)

Loading…
Cancel
Save