Rework to split layer generation and triangle rendering.
This commit is contained in:
1 parent
fa7b9345fe
commit
860ac88b61
6 files changed
+304
-86
No files matched your search
Generated
+22
@@ -145,6 +145,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"approx",
|
"approx",
|
||||||
"clap",
|
"clap",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
"stl_io",
|
"stl_io",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -175,6 +177,26 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.203"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stl_io"
|
name = "stl_io"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
approx = "0.5.1"
|
approx = "0.5.1"
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
serde = "1.0.203"
|
||||||
|
serde_derive = "1.0.203"
|
||||||
stl_io = "0.7.0"
|
stl_io = "0.7.0"
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
use stl_io::{self, Vertex};
|
||||||
|
|
||||||
|
use crate::mushroom::Mushroom;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Layer {
|
||||||
|
/// Stalk height in millimeters. Zero for default.
|
||||||
|
pub center: Vertex,
|
||||||
|
|
||||||
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
|
pub radius: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
return Self {
|
||||||
|
center: Vertex::new([1.0, 0.0, 0.0]),
|
||||||
|
radius: 0.0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub struct Layers {
|
||||||
|
pub base: Vertex,
|
||||||
|
pub layers: Vec<Layer>,
|
||||||
|
pub top: Vertex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layers {
|
||||||
|
pub fn max_radius(&self) -> f32 {
|
||||||
|
let mut max_radius: f32 = 0.0;
|
||||||
|
for layer in &self.layers {
|
||||||
|
max_radius = max_radius.max(layer.radius);
|
||||||
|
}
|
||||||
|
return max_radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stalk_layers(mushroom: &Mushroom, base: &Vertex, _resolution: f32) -> Vec<Layer> {
|
||||||
|
let stalk_top = Vertex::new([base[0], base[1], base[2] + mushroom.stalk_h]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
Layer {
|
||||||
|
center: base.clone(),
|
||||||
|
radius: mushroom.stalk_r,
|
||||||
|
},
|
||||||
|
Layer {
|
||||||
|
center: stalk_top,
|
||||||
|
radius: mushroom.stalk_r,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cap_layers(mushroom: &Mushroom, base: &Vertex, _resolution: f32) -> Vec<Layer> {
|
||||||
|
let top = Vertex::new([base[0], base[1], base[2] + mushroom.cap_h]);
|
||||||
|
|
||||||
|
return [
|
||||||
|
Layer {
|
||||||
|
center: base.clone(),
|
||||||
|
radius: mushroom.cap_r,
|
||||||
|
},
|
||||||
|
Layer {
|
||||||
|
center: top,
|
||||||
|
radius: mushroom.cap_r,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mushroom(mushroom: &Mushroom, resolution: f32) -> Layers {
|
||||||
|
let _vertical_steps = (mushroom.height() / resolution) as u32;
|
||||||
|
|
||||||
|
let base = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
|
|
||||||
|
// Get layers for the stalk.
|
||||||
|
let mut stalk_layers = stalk_layers(mushroom, &base, resolution);
|
||||||
|
|
||||||
|
// Get layers for the cap.
|
||||||
|
let cap_base = stalk_layers.last().unwrap().center;
|
||||||
|
let mut cap_layers = cap_layers(mushroom, &cap_base, resolution);
|
||||||
|
|
||||||
|
// The very top of the mushroom is directly above the top layer of the cap (or even with it)
|
||||||
|
// at the exact height of the total mushroom.
|
||||||
|
let cap_top = &cap_layers.last().unwrap().center;
|
||||||
|
let top = Vertex::new([cap_top[0], cap_top[1], base[2] + mushroom.height()]);
|
||||||
|
|
||||||
|
// Assemble the final Layers object.
|
||||||
|
let mut layers: Vec<Layer> = Vec::new();
|
||||||
|
layers.append(&mut stalk_layers);
|
||||||
|
layers.append(&mut cap_layers);
|
||||||
|
return Layers { base, top, layers };
|
||||||
|
}
|
||||||
+76
-47
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod layers;
|
||||||
pub mod mushroom;
|
pub mod mushroom;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
|
||||||
@@ -6,8 +7,30 @@ use clap::Parser;
|
|||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
extern crate approx;
|
extern crate approx;
|
||||||
|
pub use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
// TODO: Investigate https://github.com/virtualritz/polyhedron-ops.
|
fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
|
||||||
|
// Define the mushroom.
|
||||||
|
let mushroom = mushroom_from_args(&args);
|
||||||
|
|
||||||
|
// Render the mushroom.
|
||||||
|
// let mesh = render::cylinder(mushroom.stalk_h, mushroom.stalk_d, 128);
|
||||||
|
let layers = layers::mushroom(&mushroom, 0.1);
|
||||||
|
let mesh = render::render(&layers, 0.1);
|
||||||
|
|
||||||
|
// Save output to file.
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&args.output)
|
||||||
|
.unwrap();
|
||||||
|
stl_io::write_stl(&mut file, mesh.iter()).unwrap();
|
||||||
|
|
||||||
|
println!("Generated from: {:?}", mushroom);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
@@ -35,6 +58,10 @@ struct Args {
|
|||||||
/// Stalk diameter in millimeters. Zero for default.
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
#[arg(long, default_value_t = 0)]
|
#[arg(long, default_value_t = 0)]
|
||||||
cap_d: u32,
|
cap_d: u32,
|
||||||
|
|
||||||
|
/// Shape of the cap.
|
||||||
|
#[arg(long, default_value_t, value_enum)]
|
||||||
|
cap_style: mushroom::CapStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mushroom_from_args(args: &Args) -> Mushroom {
|
fn mushroom_from_args(args: &Args) -> Mushroom {
|
||||||
@@ -48,52 +75,33 @@ fn mushroom_from_args(args: &Args) -> Mushroom {
|
|||||||
args.height as f32 * 0.66
|
args.height as f32 * 0.66
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let stalk_d: f32 = if args.stalk_d != 0 {
|
let stalk_r: f32 = if args.stalk_d != 0 {
|
||||||
args.stalk_d as f32
|
args.stalk_d as f32
|
||||||
} else {
|
} else {
|
||||||
args.height as f32 / 10.
|
args.height as f32 / 10.
|
||||||
};
|
} / 2.0; // computer the diamter, and convert to radius.
|
||||||
let cap_h: f32 = if args.cap_h != 0 {
|
let cap_h: f32 = if args.cap_h != 0 {
|
||||||
args.cap_h as f32
|
args.cap_h as f32
|
||||||
} else {
|
} else {
|
||||||
args.height as f32 - stalk_h
|
args.height as f32 - stalk_h
|
||||||
};
|
};
|
||||||
let cap_d: f32 = if args.cap_d != 0 {
|
let cap_r: f32 = if args.cap_d != 0 {
|
||||||
args.cap_d as f32
|
args.cap_d as f32
|
||||||
} else {
|
} else {
|
||||||
args.height as f32
|
args.height as f32
|
||||||
};
|
} / 2.0; // computer the diamter, and convert to radius.
|
||||||
|
|
||||||
|
let cap_style = args.cap_style.clone();
|
||||||
|
|
||||||
Mushroom {
|
Mushroom {
|
||||||
stalk_h,
|
stalk_h,
|
||||||
stalk_d,
|
stalk_r,
|
||||||
cap_h,
|
cap_h,
|
||||||
cap_d,
|
cap_r,
|
||||||
|
cap_style,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = Args::parse();
|
|
||||||
|
|
||||||
// Define the mushroom.
|
|
||||||
let mushroom = mushroom_from_args(&args);
|
|
||||||
|
|
||||||
// Render the mushroom.
|
|
||||||
// let mesh = render::cylinder(mushroom.stalk_h, mushroom.stalk_d, 128);
|
|
||||||
let mesh = render::mushroom(&mushroom);
|
|
||||||
|
|
||||||
// Save output to file.
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(&args.output)
|
|
||||||
.unwrap();
|
|
||||||
stl_io::write_stl(&mut file, mesh.iter()).unwrap();
|
|
||||||
|
|
||||||
println!("Generated from: {:?}", mushroom);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -102,9 +110,10 @@ mod tests {
|
|||||||
fn validate_mushroom_from_args<I, T>(
|
fn validate_mushroom_from_args<I, T>(
|
||||||
string_args: I,
|
string_args: I,
|
||||||
stalk_h: f32,
|
stalk_h: f32,
|
||||||
stalk_d: f32,
|
stalk_r: f32,
|
||||||
cap_h: f32,
|
cap_h: f32,
|
||||||
cap_d: f32,
|
cap_r: f32,
|
||||||
|
cap_style: mushroom::CapStyle,
|
||||||
) where
|
) where
|
||||||
I: IntoIterator<Item = T>,
|
I: IntoIterator<Item = T>,
|
||||||
T: Into<OsString> + Clone,
|
T: Into<OsString> + Clone,
|
||||||
@@ -115,31 +124,47 @@ mod tests {
|
|||||||
result,
|
result,
|
||||||
Mushroom {
|
Mushroom {
|
||||||
stalk_h,
|
stalk_h,
|
||||||
stalk_d,
|
stalk_r,
|
||||||
cap_h,
|
cap_h,
|
||||||
cap_d
|
cap_r,
|
||||||
|
cap_style,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults() {
|
fn defaults() {
|
||||||
validate_mushroom_from_args(["prog"], 66., 10., 34., 100.);
|
validate_mushroom_from_args(
|
||||||
|
["prog"],
|
||||||
|
66.0,
|
||||||
|
5.0,
|
||||||
|
34.0,
|
||||||
|
50.0,
|
||||||
|
mushroom::CapStyle::Cylinder,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn height_only() {
|
fn height_only() {
|
||||||
validate_mushroom_from_args(["prog", "--height", "200"], 132., 20., 68., 200.);
|
validate_mushroom_from_args(
|
||||||
|
["prog", "--height", "200"],
|
||||||
|
132.0,
|
||||||
|
10.0,
|
||||||
|
68.0,
|
||||||
|
100.0,
|
||||||
|
mushroom::CapStyle::Cylinder,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn height_stalk_h() {
|
fn height_stalk_h() {
|
||||||
validate_mushroom_from_args(
|
validate_mushroom_from_args(
|
||||||
["prog", "--height", "200", "--stalk-h", "20"],
|
["prog", "--height", "200", "--stalk-h", "20"],
|
||||||
20.,
|
20.0,
|
||||||
20.,
|
10.0,
|
||||||
180.,
|
180.0,
|
||||||
200.,
|
100.0,
|
||||||
|
mushroom::CapStyle::Cylinder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,10 +172,11 @@ mod tests {
|
|||||||
fn height_cap_h() {
|
fn height_cap_h() {
|
||||||
validate_mushroom_from_args(
|
validate_mushroom_from_args(
|
||||||
["prog", "--height", "200", "--cap-h", "20"],
|
["prog", "--height", "200", "--cap-h", "20"],
|
||||||
180.,
|
180.0,
|
||||||
20.,
|
10.0,
|
||||||
20.,
|
20.0,
|
||||||
200.,
|
100.0,
|
||||||
|
mushroom::CapStyle::Cylinder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,11 +195,14 @@ mod tests {
|
|||||||
"40",
|
"40",
|
||||||
"--cap-d",
|
"--cap-d",
|
||||||
"50",
|
"50",
|
||||||
|
"--cap-style",
|
||||||
|
"dome",
|
||||||
],
|
],
|
||||||
20.,
|
20.0,
|
||||||
30.,
|
15.0,
|
||||||
40.,
|
40.0,
|
||||||
50.,
|
25.0,
|
||||||
|
mushroom::CapStyle::Dome,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+26
-2
@@ -1,14 +1,38 @@
|
|||||||
|
use serde_derive::Serialize;
|
||||||
|
|
||||||
|
#[derive(clap::ValueEnum, Clone, Default, Debug, Serialize, PartialEq)]
|
||||||
|
pub enum CapStyle {
|
||||||
|
#[default]
|
||||||
|
Cylinder,
|
||||||
|
Dome,
|
||||||
|
Pointed,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Mushroom {
|
pub struct Mushroom {
|
||||||
/// Stalk height in millimeters. Zero for default.
|
/// Stalk height in millimeters. Zero for default.
|
||||||
pub stalk_h: f32,
|
pub stalk_h: f32,
|
||||||
|
|
||||||
/// Stalk diameter in millimeters. Zero for default.
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
pub stalk_d: f32,
|
pub stalk_r: f32,
|
||||||
|
|
||||||
/// Cap height in millimeters. Zero for default.
|
/// Cap height in millimeters. Zero for default.
|
||||||
pub cap_h: f32,
|
pub cap_h: f32,
|
||||||
|
|
||||||
/// Stalk diameter in millimeters. Zero for default.
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
pub cap_d: f32,
|
pub cap_r: f32,
|
||||||
|
|
||||||
|
pub cap_style: CapStyle,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mushroom {
|
||||||
|
/// Total height of the mushroom.
|
||||||
|
pub fn height(&self) -> f32 {
|
||||||
|
return self.stalk_h + self.cap_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maximum radius of the mushroom.
|
||||||
|
pub fn radius(&self) -> f32 {
|
||||||
|
return self.stalk_r.max(self.cap_r) / 2.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+84
-37
@@ -1,6 +1,6 @@
|
|||||||
use stl_io::{self, Normal, Triangle, Vertex};
|
use stl_io::{self, Normal, Triangle, Vertex};
|
||||||
|
|
||||||
use crate::mushroom::Mushroom;
|
use crate::layers::{Layer, Layers};
|
||||||
|
|
||||||
fn new_triangle(p1: &Vertex, p2: &Vertex, p3: &Vertex) -> stl_io::Triangle {
|
fn new_triangle(p1: &Vertex, p2: &Vertex, p3: &Vertex) -> stl_io::Triangle {
|
||||||
// Set Vector U to (Triangle.p2 minus Triangle.p1)
|
// Set Vector U to (Triangle.p2 minus Triangle.p1)
|
||||||
@@ -40,10 +40,10 @@ fn vertex_on_circle(center: &Vertex, radius: f32, angle: f32) -> Vertex {
|
|||||||
return Vertex::new([x, y, z]);
|
return Vertex::new([x, y, z]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vertexes_on_circle(center: &Vertex, radius: f32, steps: u32) -> Vec<Vertex> {
|
fn vertexes_on_circle(layer: &Layer, steps: u32) -> Vec<Vertex> {
|
||||||
return radian_steps(steps)
|
return radian_steps(steps)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|angle| vertex_on_circle(center, radius, *angle))
|
.map(|angle| vertex_on_circle(&layer.center, layer.radius, *angle))
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,46 +80,35 @@ fn tube_top(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
|||||||
return tube_base(¢er, &edge);
|
return tube_base(¢er, &edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mushroom(mushroom: &Mushroom) -> Vec<Triangle> {
|
pub fn render(layers: &Layers, resolution: f32) -> Vec<Triangle> {
|
||||||
// Max size of any flat surface in mm.
|
// Top and Bottom must be a single point, and must be at least one middle layer.
|
||||||
let resolution: f32 = 1.0;
|
assert!(!layers.layers.is_empty());
|
||||||
|
|
||||||
// We always use the same number of diameter steps to ensure the number of circular vertices doesn't change,
|
// How many vertexes per circle. IE: the resolution of the circle.
|
||||||
// which might create small breaks in our model.
|
let circle_steps =
|
||||||
let diameter_steps = std::cmp::max(
|
((layers.max_radius() * std::f32::consts::TAU) / resolution).max(64.0) as u32;
|
||||||
(mushroom.cap_d * std::f32::consts::TAU * resolution) as u32,
|
|
||||||
64,
|
|
||||||
);
|
|
||||||
|
|
||||||
let stalk_base_center = Vertex::new([0.0, 0.0, 0.0]);
|
let mut layer_vertexes: Vec<Vec<Vertex>> = Vec::new();
|
||||||
let stalk_base_edge = vertexes_on_circle(&stalk_base_center, mushroom.stalk_d, diameter_steps);
|
|
||||||
let stalk_top_center = Vertex::new([
|
|
||||||
stalk_base_center[0],
|
|
||||||
stalk_base_center[1],
|
|
||||||
stalk_base_center[2] + mushroom.stalk_h,
|
|
||||||
]);
|
|
||||||
let stalk_top_edge = vertexes_on_circle(&stalk_top_center, mushroom.stalk_d, diameter_steps);
|
|
||||||
let cap_base_edge = vertexes_on_circle(&stalk_top_center, mushroom.cap_d, diameter_steps);
|
|
||||||
let cap_top_center = Vertex::new([
|
|
||||||
stalk_top_center[0],
|
|
||||||
stalk_top_center[1],
|
|
||||||
stalk_top_center[2] + mushroom.cap_h,
|
|
||||||
]);
|
|
||||||
let cap_top_edge = vertexes_on_circle(&cap_top_center, mushroom.cap_d, diameter_steps);
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
for layer in &layers.layers {
|
||||||
|
layer_vertexes.push(vertexes_on_circle(&layer, circle_steps));
|
||||||
|
}
|
||||||
|
|
||||||
result.append(&mut tube_base(&stalk_base_center, &stalk_base_edge));
|
let mut result: Vec<Triangle> = Vec::new();
|
||||||
result.append(&mut tube_sides(&stalk_base_edge, &stalk_top_edge));
|
|
||||||
result.append(&mut tube_sides(&stalk_top_edge, &cap_base_edge));
|
result.append(&mut tube_base(&layers.base, &layer_vertexes[0]));
|
||||||
result.append(&mut tube_sides(&cap_base_edge, &cap_top_edge));
|
for i in 0..layer_vertexes.len() - 1 {
|
||||||
result.append(&mut tube_top(&cap_top_center, &cap_top_edge));
|
result.append(&mut tube_sides(&layer_vertexes[i], &layer_vertexes[i + 1]));
|
||||||
|
}
|
||||||
|
result.append(&mut tube_top(&layers.top, &layer_vertexes.last().unwrap()));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use approx::relative_eq;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -196,7 +185,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vertexes_on_circle_basic() {
|
fn vertexes_on_circle_basic() {
|
||||||
let val = vertexes_on_circle(&Vertex::new([0.0, 0.0, 0.0]), 1.0, 4);
|
let val = vertexes_on_circle(
|
||||||
|
&Layer {
|
||||||
|
center: Vertex::new([0.0, 0.0, 0.0]),
|
||||||
|
radius: 1.0,
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
|
||||||
let expected = [
|
let expected = [
|
||||||
Vertex::new([1.0, 0.0, 0.0]),
|
Vertex::new([1.0, 0.0, 0.0]),
|
||||||
@@ -211,7 +206,13 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vertexes_on_circle_complex() {
|
fn vertexes_on_circle_complex() {
|
||||||
let val = vertexes_on_circle(&Vertex::new([4.0, 5.0, 6.0]), 3.0, 4);
|
let val = vertexes_on_circle(
|
||||||
|
&Layer {
|
||||||
|
center: Vertex::new([4.0, 5.0, 6.0]),
|
||||||
|
radius: 3.0,
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
|
||||||
let expected = [
|
let expected = [
|
||||||
Vertex::new([7.0, 5.0, 6.0]),
|
Vertex::new([7.0, 5.0, 6.0]),
|
||||||
@@ -229,7 +230,13 @@ mod tests {
|
|||||||
// fn tube_base(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
// fn tube_base(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
||||||
|
|
||||||
let center = Vertex::new([0.0, 0.0, 0.0]);
|
let center = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
let edge = vertexes_on_circle(¢er, 1.0, 4);
|
let edge = vertexes_on_circle(
|
||||||
|
&Layer {
|
||||||
|
center: center.clone(),
|
||||||
|
radius: 1.0,
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
|
||||||
let triangles = tube_base(¢er, &edge);
|
let triangles = tube_base(¢er, &edge);
|
||||||
|
|
||||||
@@ -244,7 +251,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn tube_top_test() {
|
fn tube_top_test() {
|
||||||
let center = Vertex::new([0.0, 0.0, 0.0]);
|
let center = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
let edge = vertexes_on_circle(¢er, 1.0, 4);
|
let edge = vertexes_on_circle(
|
||||||
|
&Layer {
|
||||||
|
center: center.clone(),
|
||||||
|
radius: 1.0,
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
|
||||||
let triangles = tube_top(¢er, &edge);
|
let triangles = tube_top(¢er, &edge);
|
||||||
|
|
||||||
@@ -255,4 +268,38 @@ mod tests {
|
|||||||
assert_eq!(t.normal, Vertex::new([0.0, 0.0, 1.0]));
|
assert_eq!(t.normal, Vertex::new([0.0, 0.0, 1.0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn render_test() {
|
||||||
|
let layers = Layers {
|
||||||
|
base: Vertex::new([0.0, 0.0, 0.0]),
|
||||||
|
layers: [
|
||||||
|
Layer {
|
||||||
|
center: Vertex::new([0.0, 0.0, 1.0]),
|
||||||
|
radius: 1.0,
|
||||||
|
},
|
||||||
|
Layer {
|
||||||
|
center: Vertex::new([0.0, 0.0, 2.0]),
|
||||||
|
radius: 1.0,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.to_vec(),
|
||||||
|
top: Vertex::new([0.0, 0.0, 3.0]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mesh = render(&layers, 10.0); // Large resolution ensures minimum steps of 64.
|
||||||
|
|
||||||
|
// 64 triangles for top, 128 for sides, 64 for bottom.
|
||||||
|
assert_eq!(mesh.len(), 64 + 128 + 64);
|
||||||
|
|
||||||
|
for i in 0..mesh.len() {
|
||||||
|
if i < 64 {
|
||||||
|
assert!(mesh[i].normal[2] < 0.0) // Bottom triangles point down.
|
||||||
|
} else if i < (64 + 128) {
|
||||||
|
assert!(mesh[i].normal[2] == 0.0) // side triangles point out.
|
||||||
|
} else {
|
||||||
|
assert!(mesh[i].normal[2] > 0.0) // top triangles point up.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user