-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* cubes are cuboids which have the same side length * worked through clippy's complaints * Create rust.yml * fixed breaking test from conflict resolution * added origin point to cube and cuboid * readme * Update README.md * Update README.md * implemented face abstraction for cuboid * moved prism to using faces * clippy lints * initializing points to be used in sphere, need to come up with a method for making faces * abstracting away rendering objs to own function * breaking up into lib file, and made tests less error prone
- Loading branch information
1 parent
cc64876
commit 65f3f1e
Showing
9 changed files
with
294 additions
and
143 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,6 @@ on: | |
push: | ||
branches: [ "master" ] | ||
pull_request: | ||
|
||
env: | ||
CARGO_TERM_COLOR: always | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Kodama | ||
|
||
Kodama is a language for describing 3D models. | ||
There is no spec, or CLI as of now. As requirements are evaluated, the spec will be refined to fit users' desires. | ||
|
||
## Mission Statement | ||
|
||
Due to the large complexity of 3D modelling tools such as Blender, it is difficult for beginners to enter the space. | ||
It has been observed in fields such as programming that there is a clear progression curve in terms of the usability of tools. | ||
|
||
For example in programming languages, we do not encourage beginners to learn rust or haskell. | ||
We suggest languages such as Python, JavaScript, or C. | ||
This is because these languages provide a simple interface for writing programs, with the option of using more complicated features of the language. | ||
|
||
This is contrasted with languages such as rust, or haskell, where the minimum level of knowledge for writing programs is much higher. | ||
|
||
This is often called the skill floor. | ||
|
||
It is not common for applications to have a high skill floor. This is often because they use a GUI for all operations. | ||
GUI applications need to have all options visible, or accessible in a short amount of clicks, or keyboard shortcuts. | ||
|
||
As applications scale in complexity, their GUIs have to reflect that. | ||
|
||
It is often necessary to allow users to create interesting things. | ||
|
||
This is in contrast to programming languages, which can be seen as a subset of text based applications. | ||
All the UI of a programming language is captured in a set of UTF-8 encoded keywords, and grammar. | ||
|
||
This then means that no bespoke tools are mandatory for writing programs. | ||
This README was written in vim, but it could easily have been written in vscode, notepad, or even by hovering a magnet over my laptop. | ||
|
||
Kodama aims to become a tool for users to describe 3D models in text. | ||
This allows beginners to only be exposed to the parts of the language they need for their specific case, with any extra features only a google or docs search away. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
cargo clippy -- \ | ||
-Wclippy::all \ | ||
-Wclippy::restriction \ | ||
-Wclippy::correctness \ | ||
-Wclippy::pedantic \ | ||
-Wclippy::nursery \ | ||
-Wclippy::cargo \ | ||
-Wclippy::suspicious \ | ||
-Wclippy::perf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
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<Point>, | ||
indexes: Vec<u32>, | ||
} | ||
|
||
impl Face { | ||
pub fn new(points: Vec<Point>, indexes: Vec<u32>) -> 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::<Vec<String>>() | ||
.join(" "), | ||
); | ||
result | ||
} | ||
} | ||
|
||
fn vertex_string(points: Vec<Point>) -> String { | ||
points | ||
.into_iter() | ||
.map(Point::to_obj_string) | ||
.collect::<Vec<String>>() | ||
.join("\n") | ||
} | ||
|
||
|
||
fn render_obj(points: Vec<Point>, faces: Vec<Face>) -> String { | ||
format!( | ||
r#"{0} | ||
{1} | ||
"#, | ||
vertex_string(points.to_vec()), | ||
faces | ||
.into_iter() | ||
.map(Face::to_obj_string) | ||
.collect::<Vec<String>>() | ||
.join("\n") | ||
) | ||
} | ||
|
||
fn sphere(origin: Point, radius: f32, _detail: u32) -> Result<String, String> { | ||
// 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<String, String> { | ||
println!("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<String, String> { | ||
println!("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::<f32>().expect("invalid value given"), | ||
) | ||
.unwrap(), | ||
), | ||
"cuboid" => result.push_str( | ||
&cuboid( | ||
Point::new(0.0, 0.0, 0.0), | ||
tokens[1].parse::<f32>().expect("non numeric value given"), | ||
tokens[2].parse::<f32>().expect("non numeric value given"), | ||
tokens[3].parse::<f32>().expect("non numeric value given"), | ||
) | ||
.unwrap(), | ||
), | ||
"cone" => result.push_str(&cone( | ||
Point::new(0.0, 0.0, 0.0), | ||
0, | ||
tokens[1].parse::<f32>().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]), | ||
} | ||
} | ||
result | ||
} |
Oops, something went wrong.