Skip to content

Commit

Permalink
File output (#6)
Browse files Browse the repository at this point in the history
* creating file output + refactoring

* very simple clap implementation
  • Loading branch information
coillteoir authored Oct 29, 2024
1 parent 9d3cc68 commit 5670c15
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 119 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/result
2 changes: 2 additions & 0 deletions annoying.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ cargo clippy -- \
-Wclippy::nursery \
-Wclippy::cargo \
-Wclippy::suspicious \
-Wclippy::complexity \
-Wclippy::style \
-Wclippy::perf
26 changes: 26 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
description = "A very basic flake";

inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
};

outputs = {
self,
nixpkgs,
}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.${system}.default = pkgs.mkShell {
buildInputs = [
pkgs.vim
pkgs.cargo
pkgs.clippy
pkgs.rustfmt
pkgs.f3d
pkgs.git
];
};
packages.x86_64-linux.default = pkgs.rustPlatform.buildRustPackage rec {
name = "kodama";
src = ./.;

cargoHash = "sha256-U9Un9x9EfIrJ2Zmem935SIes3KF2Aq8eip5u4PkBWFI=";
};
};
}
39 changes: 3 additions & 36 deletions src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,9 @@
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::<Vec<char>>();

if braces.len() % 2 != 0 {
return Err(String::from("invalid amount of braces"));
}
let mut stack = Vec::<char>::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<String, String> {
let _ = validate_braces(data);
Ok(String::from("yay"))
}

pub fn compile(data: &str) -> String {
pub fn compile(data: &str) -> Result<String, String> {
let mut result = String::new();
let _ = parser(data);
let lines = data.split('\n');

for line in lines {
let tokens: Vec<&str> = line.split(' ').collect();
match *tokens.first().unwrap() {
Expand Down Expand Up @@ -73,5 +40,5 @@ pub fn compile(data: &str) -> String {
&_ => eprintln!("{} not supported", tokens[0]),
}
}
result
Ok(result)
}
34 changes: 9 additions & 25 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,23 @@
use clap::Parser;
use kodama::compiler::compile;
use std::ffi::OsStr;
use kodama::compiler::*;
use std::fs;
use std::path::Path;

#[derive(Parser)]
#[command(
author = "David Lynch",
about = "3d modelling but epic",
long_about = "lengthy epic"
)]
#[command(author = "David Lynch", about = "3d modelling but epic")]
struct Args {
#[arg(help = "source file")]
#[arg(short, long, help = "source file")]
source: String,
#[arg(help = "output file", default_value = "")]
#[arg(short, long, help = "output file")]
output: String,
}

fn main() {
println!("{}", compile("sphere 1"));

let args = Args::parse();

let source_path = Path::new(&args.source);
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");
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(other) => eprintln!("file type {other} not supported"),
None => println!("{}", compile(&source)),
let contents = fs::read_to_string(args.source).expect("File not found");
if let Ok(result) = compile(&contents) {
if let Err(e) = fs::write(args.output, result) {
panic!("{}", e)
}
}
}
109 changes: 52 additions & 57 deletions src/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,31 @@ impl Point {
pub const fn new(x: f32, y: f32, z: f32) -> Self {
Self { x, y, z }
}
pub fn to_obj_string(self) -> String {
pub fn to_obj_string(&self) -> String {
format!("v {0} {1} {2}", self.x, self.y, self.z)
}
}

#[derive(Clone)]
struct Face {
points: Vec<Point>,
indexes: Vec<u32>,
struct Face<'lt> {
points: &'lt [Point],
indexes: &'lt [u32],
}

impl Face {
pub fn new(points: Vec<Point>, indexes: Vec<u32>) -> Self {
impl<'lt> Face<'lt> {
pub fn new(points: &'lt [Point], indexes: &'lt [u32]) -> Self {
Self { points, indexes }
}
pub fn to_obj_string(self) -> String {

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)))
.iter()
.map(|i: &u32| -> String {
format!("-{0}", (self.points.len() as u64 - u64::from(*i)))
})
.collect::<Vec<String>>()
.join(" "),
Expand All @@ -44,23 +44,23 @@ impl Face {
}
}

fn vertex_string(points: Vec<Point>) -> String {
fn vertex_string(points: &[Point]) -> String {
points
.into_iter()
.iter()
.map(Point::to_obj_string)
.collect::<Vec<String>>()
.join("\n")
}

fn render_obj(points: Vec<Point>, faces: Vec<Face>) -> String {
fn render_obj(points: &[Point], faces: &[Face]) -> String {
format!(
r#"{0}
{1}
"#,
vertex_string(points.to_vec()),
vertex_string(points),
faces
.into_iter()
.map(Face::to_obj_string)
.iter()
.map(|f: &Face| Face::to_obj_string(f))
.collect::<Vec<String>>()
.join("\n")
)
Expand All @@ -73,75 +73,70 @@ pub fn sphere(origin: Point, radius: f32, _detail: u32) -> Result<String, String
// Create faces procedurally
// To start we'll create a simple octahedron

let points = vec![
//top
Point::new(origin.x, origin.y + (radius * (PI / 2.0).sin()), origin.z),
//bottom
Point::new(origin.x, origin.y + (radius * (PI / -2.0).sin()), origin.z),
//north
Point::new(origin.x, origin.y, origin.z + (radius * (PI / 2.0).sin())),
//south
Point::new(origin.x, origin.y, origin.z + (radius * (PI / -2.0).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),
];
let top = Point::new(origin.x, origin.y + (radius * (PI / 2.).sin()), origin.z);
let bottom = Point::new(origin.x, origin.y + (radius * (PI / -2.).sin()), origin.z);
let north = Point::new(origin.x, origin.y, origin.z + (radius * (PI / 2.).sin()));
let south = Point::new(origin.x, origin.y, origin.z + (radius * (PI / -2.).sin()));
let east = Point::new(origin.x + (radius * (0.0_f32).cos()), origin.y, origin.z);
let west = Point::new(origin.x + (radius * (PI).cos()), origin.y, origin.z);

let points = [top, bottom, north, south, east, west];

let faces = [
// top, north, east
Face::new(points.clone(), vec![0, 2, 4]),
Face::new(&points, &[0, 2, 4]),
// top, north, west
Face::new(points.clone(), vec![0, 2, 5]),
Face::new(&points, &[0, 2, 5]),
// top, south, east
Face::new(points.clone(), vec![0, 3, 4]),
Face::new(&points, &[0, 3, 4]),
// top, south, west
Face::new(points.clone(), vec![0, 3, 5]),
Face::new(&points, &[0, 3, 5]),
// bottom, north, east
Face::new(points.clone(), vec![1, 2, 4]),
Face::new(&points, &[1, 2, 4]),
// bottom, north, west
Face::new(points.clone(), vec![1, 2, 5]),
Face::new(&points, &[1, 2, 5]),
// bottom, south, east
Face::new(points.clone(), vec![1, 3, 4]),
Face::new(&points, &[1, 3, 4]),
// bottom, south, west
Face::new(points.clone(), vec![1, 3, 5]),
Face::new(&points, &[1, 3, 5]),
];

Ok(render_obj(points, faces.to_vec()))
Ok(render_obj(&points, &faces))
}

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.x + (4. * PI / 3.).sin(),
origin.y,
(2.0 * -PI / 3.0).cos(),
(2. * -PI / 3.).cos(),
),
Point::new(
origin.x + (2.0 * PI / 3.0).sin(),
origin.x + (2. * PI / 3.).sin(),
origin.y,
(2.0 * -PI / 3.0).cos(),
(2. * -PI / 3.).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]),
Face::new(&points, &[1, 2, 3]),
Face::new(&points, &[0, 2, 3]),
Face::new(&points, &[0, 1, 3]),
Face::new(&points, &[0, 2, 1]),
];

render_obj(points.to_vec(), faces.to_vec())
render_obj(&points, &faces)
}

pub fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result<String, String> {
if sx <= 0.0 || sy <= 0.0 || sz <= 0.0 {
if sx <= 0. || sy <= 0. || sz <= 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),
Expand All @@ -154,15 +149,15 @@ pub fn cuboid(origin: Point, sx: f32, sy: f32, sz: f32) -> Result<String, String
];

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]),
Face::new(&points, &[0, 1, 2, 3]),
Face::new(&points, &[7, 6, 5, 4]),
Face::new(&points, &[4, 5, 1, 0]),
Face::new(&points, &[3, 7, 4, 0]),
Face::new(&points, &[3, 2, 6, 7]),
Face::new(&points, &[6, 2, 1, 5]),
];

Ok(render_obj(points.to_vec(), faces.to_vec()))
Ok(render_obj(&points, &faces))
}

pub fn cube(origin: Point, size: f32) -> Result<String, String> {
Expand Down
2 changes: 1 addition & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod tests {
fn run_test(input_path: String, result_path: String) {
let input = fs::read_to_string(input_path).expect("could not load file");
let result = fs::read_to_string(result_path).expect("could not load file");
assert_eq!(compile(&input), result);
assert_eq!(compile(&input), Ok(result));
}
#[test]
fn walk_tests() {
Expand Down

0 comments on commit 5670c15

Please sign in to comment.