From 2cd25867f3a91877f5bd5f6919084a2e5e9a3425 Mon Sep 17 00:00:00 2001 From: David Lynch Date: Sat, 18 May 2024 18:18:40 +0100 Subject: [PATCH 1/4] starting work on CLI --- Cargo.toml | 2 -- src/lib.rs | 13 ++++++------- src/main.rs | 22 +++++++++++++++++++++- tests/test.rs | 6 +++--- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c285528..e436643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,4 @@ name = "kodama" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] diff --git a/src/lib.rs b/src/lib.rs index ee75309..c81a10f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,7 +52,6 @@ fn vertex_string(points: Vec) -> String { .join("\n") } - fn render_obj(points: Vec, faces: Vec) -> String { format!( r#"{0} @@ -138,11 +137,11 @@ fn cone(origin: Point, _detail: i32, _radius: f32) -> String { } fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result { - println!("GENERATING CUBOID"); + eprintln!("GENERATING CUBOID"); if sx <= 0.0 || sy <= 0.0 || sz <= 0.0 { - return Err( - String::from("could not generate cuboid, side length less than or equal to zero"), - ); + return Err(String::from( + "could not generate cuboid, side length less than or equal to zero", + )); } let points = [ Point::new(origin.x, origin.y + sy, origin.z + sz), @@ -168,7 +167,7 @@ fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result { } fn cube(origin: Point, size: f32) -> Result { - println!("GENERATING CUBE"); + eprintln!("GENERATING CUBE"); if size <= 0.0 { return Err("ERROR: cannot generate cube of size less than zero".to_string()); } @@ -204,7 +203,7 @@ pub fn compile(data: &str) -> String { tokens[1].parse::().expect("non numeric value given"), )), "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 10.0, 10).unwrap()), - &_ => println!("{} not supported", tokens[0]), + &_ => eprintln!("{} not supported", tokens[0]), } } result diff --git a/src/main.rs b/src/main.rs index 9761898..2574beb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,25 @@ use kodama::compile; +use std::env; +use std::ffi::OsStr; +use std::fs; +use std::path::Path; fn main() { - println!("{}", compile("sphere")); + let args: Vec = env::args().collect(); + let source_path = Path::new(&args[1]); + let mut output_path = Path::new(""); + if args.len() == 3 { + output_path = Path::new(&args[2]); + } + if source_path.extension().and_then(OsStr::to_str) != Some("kda") { + eprintln!("please specify a kodama source file"); + return; + } + let source = fs::read_to_string(&source_path).expect("couldn't load source file"); + + match output_path.extension().and_then(OsStr::to_str) { + Some("obj") => println!("{}", compile(&source)), + Some(e) => eprintln!("file type {} not supported", e), + None => eprintln!("please specify an output file type"), + } } diff --git a/tests/test.rs b/tests/test.rs index 4add9ae..95b8559 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { - use std::fs; use kodama::compile; + use std::fs; fn run_test(input_path: String, result_path: String) { let input = fs::read_to_string(&input_path).expect("could not load file"); @@ -15,8 +15,8 @@ mod tests { Ok(entries) => { for entry in entries { let path = entry.expect("could not open dir").path(); - if !path.is_dir(){ - continue + if !path.is_dir() { + continue; } let input = path.join("main.kda"); let output = path.join("result.obj"); From b62353084af98fc407a4b3eb7a02ed69883a8d5b Mon Sep 17 00:00:00 2001 From: David Lynch Date: Fri, 7 Jun 2024 00:13:31 +0100 Subject: [PATCH 2/4] added clap --- Cargo.lock | 230 +++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/compiler.rs | 70 ++++++++++ src/lib.rs | 212 +--------------------------- src/main.rs | 26 ++-- src/shape.rs | 173 +++++++++++++++++++++++ tests/cone/main.kda | 1 + tests/{prism => cone}/result.mtl | 0 tests/{prism => cone}/result.obj | 0 tests/cuboid/main.kda | 2 +- tests/prism/main.kda | 1 - tests/test.rs | 2 +- 12 files changed, 498 insertions(+), 221 deletions(-) create mode 100644 src/compiler.rs create mode 100644 src/shape.rs create mode 100644 tests/cone/main.kda rename tests/{prism => cone}/result.mtl (100%) rename tests/{prism => cone}/result.obj (100%) delete mode 100644 tests/prism/main.kda diff --git a/Cargo.lock b/Cargo.lock index 8c050c8..ee024db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,236 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9689a29b593160de5bc4aacab7b5d54fb52231de70122626c178e6a368994c7" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5387378c84f6faa26890ebf9f0a92989f8873d4d380467bcd0d8d8620424df" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "kodama" version = "0.1.0" +dependencies = [ + "clap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml index e436643..25d3ca3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +clap = {version = "4.5.6",features = ["derive"]} + diff --git a/src/compiler.rs b/src/compiler.rs new file mode 100644 index 0000000..ef369d1 --- /dev/null +++ b/src/compiler.rs @@ -0,0 +1,70 @@ +use crate::shape::{cone, cube, cuboid, sphere, Point}; + +#[derive(Debug, Clone)] +struct Token {} + +fn validate_braces(source: &str) -> Result<(), String> { + let braces = String::from(source) + .chars() + .filter(|c| (*c == '{' || *c == '}')) + .collect::>(); + + if braces.len() % 2 != 0 { + return Err(String::from("invalid amount of braces")); + } + let mut stack = Vec::::new(); + for brace in braces.into_iter() { + if brace == '{' { + stack.push(brace); + } + + if brace == '}' { + stack.pop(); + } + } + if !stack.is_empty() { + return Err(String::from("mismatched braces")); + } + Ok(()) +} + +fn parser(data: &str) -> Result { + validate_braces(data); + Ok(String::from("yay")) +} + +pub fn compile(data: &str) -> String { + let mut result = String::new(); + parser(data); + let lines = data.split('\n'); + for line in lines { + let tokens: Vec<&str> = line.split(' ').collect(); + match *tokens.first().unwrap() { + "cube" => result.push_str( + &cube( + Point::new(0.0, 0.0, 0.0), + tokens[1].parse::().expect("invalid value given"), + ) + .unwrap(), + ), + "cuboid" => result.push_str( + &cuboid( + Point::new(0.0, 0.0, 0.0), + tokens[1].parse::().expect("non numeric value given"), + tokens[2].parse::().expect("non numeric value given"), + tokens[3].parse::().expect("non numeric value given"), + ) + .unwrap(), + ), + "cone" => result.push_str(&cone( + Point::new(0.0, 0.0, 0.0), + 0, + tokens[1].parse::().expect("non numeric value given"), + )), + "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 10.0, 10).unwrap()), + "#" => todo!(), + &_ => eprintln!("{} not supported", tokens[0]), + } + } + result +} diff --git a/src/lib.rs b/src/lib.rs index c81a10f..2737e86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,210 +1,2 @@ -use std::f32::consts::PI; - -#[derive(Clone, Copy, Debug)] -struct Point { - x: f32, - y: f32, - z: f32, -} - -impl Point { - const fn new(x: f32, y: f32, z: f32) -> Self { - Self { x, y, z } - } - pub fn to_obj_string(self) -> String { - format!("v {0} {1} {2}", self.x, self.y, self.z) - } -} - -#[derive(Clone)] -struct Face { - points: Vec, - indexes: Vec, -} - -impl Face { - pub fn new(points: Vec, indexes: Vec) -> Self { - Self { points, indexes } - } - pub fn to_obj_string(self) -> String { - let mut result = String::from("f "); - - result.push_str( - &self - .indexes - .clone() - .into_iter() - .map(|i: u32| -> String { - format!("-{0}", (self.points.len() as u64 - u64::from(i))) - }) - .collect::>() - .join(" "), - ); - result - } -} - -fn vertex_string(points: Vec) -> String { - points - .into_iter() - .map(Point::to_obj_string) - .collect::>() - .join("\n") -} - -fn render_obj(points: Vec, faces: Vec) -> String { - format!( - r#"{0} -{1} -"#, - vertex_string(points.to_vec()), - faces - .into_iter() - .map(Face::to_obj_string) - .collect::>() - .join("\n") - ) -} - -fn sphere(origin: Point, radius: f32, _detail: u32) -> Result { - // top and bottom point - // create vertical "strips" down the sphere, - // rotating around the Z axis - // Create faces procedurally - - let points = [ - //top - Point::new(origin.x, origin.y + (radius * (2.0 * PI).sin()), origin.z), - //bottom - Point::new(origin.x, origin.y + (radius * (-2.0 * PI).sin()), origin.z), - //north - Point::new(origin.x, origin.y, origin.z + (radius * (2.0 * PI).sin())), - //south - Point::new(origin.x, origin.y, origin.z + (radius * (-2.0 * PI).sin())), - //east - Point::new(origin.x + (radius * (0.0_f32).cos()), origin.y, origin.z), - //west - Point::new(origin.x + (radius * (PI).cos()), origin.y, origin.z), - ] - .to_vec(); - - let faces = [ - // top, north, east - Face::new(points.clone(), vec![0, 2, 4]), - // top, north, west - Face::new(points.clone(), vec![0, 2, 5]), - // top, south, east - Face::new(points.clone(), vec![0, 3, 4]), - // top, south, west - Face::new(points.clone(), vec![0, 3, 5]), - // bottom, north, east - Face::new(points.clone(), vec![1, 2, 4]), - // bottom, north, west - Face::new(points.clone(), vec![1, 2, 5]), - // bottom, south, east - Face::new(points.clone(), vec![1, 3, 4]), - // bottom, south, west - Face::new(points.clone(), vec![1, 3, 5]), - ]; - - Ok(render_obj(points, faces.to_vec())) -} - -fn cone(origin: Point, _detail: i32, _radius: f32) -> String { - let points = [ - Point::new(origin.x, origin.y + (PI / 3.0).sin(), origin.z), - Point::new(origin.x, origin.y, origin.z + 0.0_f32.cos()), - Point::new( - origin.x + (4.0 * PI / 3.0).sin(), - origin.y, - (2.0 * -PI / 3.0).cos(), - ), - Point::new( - origin.x + (2.0 * PI / 3.0).sin(), - origin.y, - (2.0 * -PI / 3.0).cos(), - ), - ]; - - let faces = [ - Face::new(points.to_vec(), vec![1, 2, 3]), - Face::new(points.to_vec(), vec![0, 2, 3]), - Face::new(points.to_vec(), vec![0, 1, 3]), - Face::new(points.to_vec(), vec![0, 2, 1]), - ]; - - render_obj(points.to_vec(), faces.to_vec()) -} - -fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result { - eprintln!("GENERATING CUBOID"); - if sx <= 0.0 || sy <= 0.0 || sz <= 0.0 { - return Err(String::from( - "could not generate cuboid, side length less than or equal to zero", - )); - } - let points = [ - Point::new(origin.x, origin.y + sy, origin.z + sz), - Point::new(origin.x, origin.y, origin.z + sz), - Point::new(origin.x + sx, origin.y, origin.z + sz), - Point::new(origin.x + sx, origin.y + sy, origin.z + sz), - Point::new(origin.x, origin.y + sy, origin.z), - Point::new(origin.x, origin.y, origin.z), - Point::new(origin.x + sx, origin.y, origin.z), - Point::new(origin.x + sx, origin.y + sy, origin.z), - ]; - - let faces = [ - Face::new(points.to_vec(), vec![0, 1, 2, 3]), - Face::new(points.to_vec(), vec![7, 6, 5, 4]), - Face::new(points.to_vec(), vec![4, 5, 1, 0]), - Face::new(points.to_vec(), vec![3, 7, 4, 0]), - Face::new(points.to_vec(), vec![3, 2, 6, 7]), - Face::new(points.to_vec(), vec![6, 2, 1, 5]), - ]; - - Ok(render_obj(points.to_vec(), faces.to_vec())) -} - -fn cube(origin: Point, size: f32) -> Result { - eprintln!("GENERATING CUBE"); - if size <= 0.0 { - return Err("ERROR: cannot generate cube of size less than zero".to_string()); - } - cuboid(origin, size, size, size) -} - -pub fn compile(data: &str) -> String { - let mut result = String::new(); - - let lines = data.split('\n'); - for line in lines { - let tokens: Vec<&str> = line.split(' ').collect(); - match *tokens.first().unwrap() { - "cube" => result.push_str( - &cube( - Point::new(0.0, 0.0, 0.0), - tokens[1].parse::().expect("invalid value given"), - ) - .unwrap(), - ), - "cuboid" => result.push_str( - &cuboid( - Point::new(0.0, 0.0, 0.0), - tokens[1].parse::().expect("non numeric value given"), - tokens[2].parse::().expect("non numeric value given"), - tokens[3].parse::().expect("non numeric value given"), - ) - .unwrap(), - ), - "cone" => result.push_str(&cone( - Point::new(0.0, 0.0, 0.0), - 0, - tokens[1].parse::().expect("non numeric value given"), - )), - "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 10.0, 10).unwrap()), - &_ => eprintln!("{} not supported", tokens[0]), - } - } - result -} +pub mod compiler; +pub mod shape; diff --git a/src/main.rs b/src/main.rs index 2574beb..f868fe0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,31 @@ -use kodama::compile; +use kodama::compiler::compile; +use clap::{Parser, Subcommand}; use std::env; use std::ffi::OsStr; use std::fs; use std::path::Path; +#[derive(Parser)] +#[command(author, version, about="3d modelling but epic", long_about="lengthy epic")] +struct Args { + #[arg(help="source file")] + source: String, + #[arg(help="output file")] + output: String +} + fn main() { - let args: Vec = env::args().collect(); - let source_path = Path::new(&args[1]); - let mut output_path = Path::new(""); - if args.len() == 3 { - output_path = Path::new(&args[2]); - } + + let args = Args::parse(); + + let source_path = Path::new(&args.source); + let mut output_path = Path::new(&args.output); + if source_path.extension().and_then(OsStr::to_str) != Some("kda") { eprintln!("please specify a kodama source file"); return; } - let source = fs::read_to_string(&source_path).expect("couldn't load source file"); + let source = fs::read_to_string(source_path).expect("couldn't load source file"); match output_path.extension().and_then(OsStr::to_str) { Some("obj") => println!("{}", compile(&source)), diff --git a/src/shape.rs b/src/shape.rs new file mode 100644 index 0000000..8f7570d --- /dev/null +++ b/src/shape.rs @@ -0,0 +1,173 @@ +use std::f32::consts::PI; + +#[derive(Clone, Copy, Debug)] +pub struct Point { + x: f32, + y: f32, + z: f32, +} + +impl Point { + pub const fn new(x: f32, y: f32, z: f32) -> Self { + Self { x, y, z } + } + pub fn to_obj_string(self) -> String { + format!("v {0} {1} {2}", self.x, self.y, self.z) + } +} + +#[derive(Clone)] +struct Face { + points: Vec, + indexes: Vec, +} + +impl Face { + pub fn new(points: Vec, indexes: Vec) -> Self { + Self { points, indexes } + } + pub fn to_obj_string(self) -> String { + let mut result = String::from("f "); + + result.push_str( + &self + .indexes + .clone() + .into_iter() + .map(|i: u32| -> String { + format!("-{0}", (self.points.len() as u64 - u64::from(i))) + }) + .collect::>() + .join(" "), + ); + result + } +} + +fn vertex_string(points: Vec) -> String { + points + .into_iter() + .map(Point::to_obj_string) + .collect::>() + .join("\n") +} + +fn render_obj(points: Vec, faces: Vec) -> String { + format!( + r#"{0} +{1} +"#, + vertex_string(points.to_vec()), + faces + .into_iter() + .map(Face::to_obj_string) + .collect::>() + .join("\n") + ) +} + +pub fn sphere(origin: Point, radius: f32, _detail: u32) -> Result { + // top and bottom point + // create vertical "strips" down the sphere, + // rotating around the Z axis + // Create faces procedurally + + let points = [ + //top + Point::new(origin.x, origin.y + (radius * (2.0 * PI).sin()), origin.z), + //bottom + Point::new(origin.x, origin.y + (radius * (-2.0 * PI).sin()), origin.z), + //north + Point::new(origin.x, origin.y, origin.z + (radius * (2.0 * PI).sin())), + //south + Point::new(origin.x, origin.y, origin.z + (radius * (-2.0 * PI).sin())), + //east + Point::new(origin.x + (radius * (0.0_f32).cos()), origin.y, origin.z), + //west + Point::new(origin.x + (radius * (PI).cos()), origin.y, origin.z), + ] + .to_vec(); + + let faces = [ + // top, north, east + Face::new(points.clone(), vec![0, 2, 4]), + // top, north, west + Face::new(points.clone(), vec![0, 2, 5]), + // top, south, east + Face::new(points.clone(), vec![0, 3, 4]), + // top, south, west + Face::new(points.clone(), vec![0, 3, 5]), + // bottom, north, east + Face::new(points.clone(), vec![1, 2, 4]), + // bottom, north, west + Face::new(points.clone(), vec![1, 2, 5]), + // bottom, south, east + Face::new(points.clone(), vec![1, 3, 4]), + // bottom, south, west + Face::new(points.clone(), vec![1, 3, 5]), + ]; + + Ok(render_obj(points, faces.to_vec())) +} + +pub fn cone(origin: Point, _detail: i32, _radius: f32) -> String { + let points = [ + Point::new(origin.x, origin.y + (PI / 3.0).sin(), origin.z), + Point::new(origin.x, origin.y, origin.z + 0.0_f32.cos()), + Point::new( + origin.x + (4.0 * PI / 3.0).sin(), + origin.y, + (2.0 * -PI / 3.0).cos(), + ), + Point::new( + origin.x + (2.0 * PI / 3.0).sin(), + origin.y, + (2.0 * -PI / 3.0).cos(), + ), + ]; + + let faces = [ + Face::new(points.to_vec(), vec![1, 2, 3]), + Face::new(points.to_vec(), vec![0, 2, 3]), + Face::new(points.to_vec(), vec![0, 1, 3]), + Face::new(points.to_vec(), vec![0, 2, 1]), + ]; + + render_obj(points.to_vec(), faces.to_vec()) +} + +pub fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result { + if sx <= 0.0 || sy <= 0.0 || sz <= 0.0 { + return Err(String::from( + "could not generate cuboid, side length less than or equal to zero", + )); + } + let points = [ + Point::new(origin.x, origin.y + sy, origin.z + sz), + Point::new(origin.x, origin.y, origin.z + sz), + Point::new(origin.x + sx, origin.y, origin.z + sz), + Point::new(origin.x + sx, origin.y + sy, origin.z + sz), + Point::new(origin.x, origin.y + sy, origin.z), + Point::new(origin.x, origin.y, origin.z), + Point::new(origin.x + sx, origin.y, origin.z), + Point::new(origin.x + sx, origin.y + sy, origin.z), + ]; + + let faces = [ + Face::new(points.to_vec(), vec![0, 1, 2, 3]), + Face::new(points.to_vec(), vec![7, 6, 5, 4]), + Face::new(points.to_vec(), vec![4, 5, 1, 0]), + Face::new(points.to_vec(), vec![3, 7, 4, 0]), + Face::new(points.to_vec(), vec![3, 2, 6, 7]), + Face::new(points.to_vec(), vec![6, 2, 1, 5]), + ]; + + Ok(render_obj(points.to_vec(), faces.to_vec())) +} + +pub fn cube(origin: Point, size: f32) -> Result { + if size <= 0.0 { + return Err("ERROR: cannot generate cube of size less than zero".to_string()); + } + cuboid(origin, size, size, size) +} diff --git a/tests/cone/main.kda b/tests/cone/main.kda new file mode 100644 index 0000000..d806999 --- /dev/null +++ b/tests/cone/main.kda @@ -0,0 +1 @@ +cone 3 1 1 diff --git a/tests/prism/result.mtl b/tests/cone/result.mtl similarity index 100% rename from tests/prism/result.mtl rename to tests/cone/result.mtl diff --git a/tests/prism/result.obj b/tests/cone/result.obj similarity index 100% rename from tests/prism/result.obj rename to tests/cone/result.obj diff --git a/tests/cuboid/main.kda b/tests/cuboid/main.kda index 5797ab1..8f72150 100644 --- a/tests/cuboid/main.kda +++ b/tests/cuboid/main.kda @@ -1 +1 @@ -cuboid 2 3 4 +cuboid 2 3 4 diff --git a/tests/prism/main.kda b/tests/prism/main.kda deleted file mode 100644 index 07a309b..0000000 --- a/tests/prism/main.kda +++ /dev/null @@ -1 +0,0 @@ -cone 1 diff --git a/tests/test.rs b/tests/test.rs index 95b8559..a49531a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use kodama::compile; + use kodama::compiler::compile; use std::fs; fn run_test(input_path: String, result_path: String) { From 565a9109fc7e7f62092849d8362f6919d1f0c3bc Mon Sep 17 00:00:00 2001 From: David Lynch Date: Fri, 7 Jun 2024 01:08:07 +0100 Subject: [PATCH 3/4] simple octahedron sphere --- src/compiler.rs | 6 +++--- src/main.rs | 23 ++++++++++++++--------- src/shape.rs | 14 +++++++------- tests/sphere/main.kda | 1 + tests/sphere/result.obj | 14 ++++++++++++++ tests/test.rs | 4 ++-- 6 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 tests/sphere/main.kda create mode 100644 tests/sphere/result.obj diff --git a/src/compiler.rs b/src/compiler.rs index ef369d1..1c017f6 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -29,13 +29,13 @@ fn validate_braces(source: &str) -> Result<(), String> { } fn parser(data: &str) -> Result { - validate_braces(data); + let _ = validate_braces(data); Ok(String::from("yay")) } pub fn compile(data: &str) -> String { let mut result = String::new(); - parser(data); + let _ = parser(data); let lines = data.split('\n'); for line in lines { let tokens: Vec<&str> = line.split(' ').collect(); @@ -61,7 +61,7 @@ pub fn compile(data: &str) -> String { 0, tokens[1].parse::().expect("non numeric value given"), )), - "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 10.0, 10).unwrap()), + "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 1.0, 10).unwrap()), "#" => todo!(), &_ => eprintln!("{} not supported", tokens[0]), } diff --git a/src/main.rs b/src/main.rs index f868fe0..58f056b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,25 +1,30 @@ +use clap::Parser; use kodama::compiler::compile; -use clap::{Parser, Subcommand}; -use std::env; use std::ffi::OsStr; use std::fs; use std::path::Path; #[derive(Parser)] -#[command(author, version, about="3d modelling but epic", long_about="lengthy epic")] +#[command( + author = "David Lynch", + about = "3d modelling but epic", + long_about = "lengthy epic" +)] struct Args { - #[arg(help="source file")] + #[arg(help = "source file")] source: String, - #[arg(help="output file")] - output: String + #[arg(help = "output file", default_value="")] + output: String, } fn main() { + println!("{}", compile("sphere 1")); + let args = Args::parse(); let source_path = Path::new(&args.source); - let mut output_path = Path::new(&args.output); + let output_path = Path::new(&args.output); if source_path.extension().and_then(OsStr::to_str) != Some("kda") { eprintln!("please specify a kodama source file"); @@ -29,7 +34,7 @@ fn main() { match output_path.extension().and_then(OsStr::to_str) { Some("obj") => println!("{}", compile(&source)), - Some(e) => eprintln!("file type {} not supported", e), - None => eprintln!("please specify an output file type"), + Some(other) => eprintln!("file type {other} not supported"), + None => println!("{}", compile(&source)), } } diff --git a/src/shape.rs b/src/shape.rs index 8f7570d..6706a7b 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -71,22 +71,22 @@ pub fn sphere(origin: Point, radius: f32, _detail: u32) -> Result Date: Fri, 7 Jun 2024 01:11:51 +0100 Subject: [PATCH 4/4] tidy up pre merge --- src/compiler.rs | 9 ++++++++- src/main.rs | 3 +-- src/shape.rs | 8 ++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index 1c017f6..dcae291 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -61,7 +61,14 @@ pub fn compile(data: &str) -> String { 0, tokens[1].parse::().expect("non numeric value given"), )), - "sphere" => result.push_str(&sphere(Point::new(0.0, 0.0, 0.0), 1.0, 10).unwrap()), + "sphere" => result.push_str( + &sphere( + Point::new(0.0, 0.0, 0.0), + tokens[1].parse::().expect("non numeric value given"), + 10, + ) + .unwrap(), + ), "#" => todo!(), &_ => eprintln!("{} not supported", tokens[0]), } diff --git a/src/main.rs b/src/main.rs index 58f056b..f3c454c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,12 +13,11 @@ use std::path::Path; struct Args { #[arg(help = "source file")] source: String, - #[arg(help = "output file", default_value="")] + #[arg(help = "output file", default_value = "")] output: String, } fn main() { - println!("{}", compile("sphere 1")); let args = Args::parse(); diff --git a/src/shape.rs b/src/shape.rs index 6706a7b..df230c8 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -75,13 +75,13 @@ pub fn sphere(origin: Point, radius: f32, _detail: u32) -> Result