Initial checkpoint.
The system is capable of parsing it's command line, and of generating a mesh representing the mushroom's stalk.
This commit is contained in:
1 parent
6be55d2ebe
commit
b49610be92
5 files changed
+709
-1
No files matched your search
Generated
+281
@@ -2,6 +2,287 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
|
||||||
|
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 = "approx"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "mushroom_generator"
|
name = "mushroom_generator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"approx",
|
||||||
|
"clap",
|
||||||
|
"stl_io",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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 = "stl_io"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d520eb03e632e666ac32f3c0cf018ac6dcd535ab0be1420937e2d896f44f7cf7"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
@@ -4,3 +4,6 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
approx = "0.5.1"
|
||||||
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
stl_io = "0.7.0"
|
||||||
+178
-1
@@ -1,3 +1,180 @@
|
|||||||
|
pub mod mushroom;
|
||||||
|
pub mod render;
|
||||||
|
|
||||||
|
use self::mushroom::Mushroom;
|
||||||
|
use clap::Parser;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate approx;
|
||||||
|
|
||||||
|
// TODO: Investigate https://github.com/virtualritz/polyhedron-ops.
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Clone)]
|
||||||
|
#[command(version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// File to save too.
|
||||||
|
#[arg(long, default_value = "mesh.stl")]
|
||||||
|
output: String,
|
||||||
|
|
||||||
|
/// Height of total mushroom in millimeters.
|
||||||
|
#[arg(long, default_value_t = 100)]
|
||||||
|
height: u32,
|
||||||
|
|
||||||
|
/// Stalk height in millimeters. Zero for default.
|
||||||
|
#[arg(long, default_value_t = 0)]
|
||||||
|
stalk_h: u32,
|
||||||
|
|
||||||
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
|
#[arg(long, default_value_t = 0)]
|
||||||
|
stalk_d: u32,
|
||||||
|
|
||||||
|
/// Cap height in millimeters. Zero for default.
|
||||||
|
#[arg(long, default_value_t = 0)]
|
||||||
|
cap_h: u32,
|
||||||
|
|
||||||
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
|
#[arg(long, default_value_t = 0)]
|
||||||
|
cap_d: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mushroom_from_args(args: &Args) -> Mushroom {
|
||||||
|
// Default values are all based on height.
|
||||||
|
let stalk_h: f32 = if args.stalk_h != 0 {
|
||||||
|
args.stalk_h as f32
|
||||||
|
} else {
|
||||||
|
if args.cap_h != 0 {
|
||||||
|
(args.height - args.cap_h) as f32
|
||||||
|
} else {
|
||||||
|
args.height as f32 * 0.66
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let stalk_d: f32 = if args.stalk_d != 0 {
|
||||||
|
args.stalk_d as f32
|
||||||
|
} else {
|
||||||
|
args.height as f32 / 10.
|
||||||
|
};
|
||||||
|
let cap_h: f32 = if args.cap_h != 0 {
|
||||||
|
args.cap_h as f32
|
||||||
|
} else {
|
||||||
|
args.height as f32 - stalk_h
|
||||||
|
};
|
||||||
|
let cap_d: f32 = if args.cap_d != 0 {
|
||||||
|
args.cap_d as f32
|
||||||
|
} else {
|
||||||
|
args.height as f32
|
||||||
|
};
|
||||||
|
|
||||||
|
Mushroom {
|
||||||
|
stalk_h,
|
||||||
|
stalk_d,
|
||||||
|
cap_h,
|
||||||
|
cap_d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
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);
|
||||||
|
|
||||||
|
// 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!("From: {:?}", mushroom);
|
||||||
|
println!("Generated:\n{:?}", mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
|
||||||
|
fn validate_mushroom_from_args<I, T>(
|
||||||
|
string_args: I,
|
||||||
|
stalk_h: f32,
|
||||||
|
stalk_d: f32,
|
||||||
|
cap_h: f32,
|
||||||
|
cap_d: f32,
|
||||||
|
) where
|
||||||
|
I: IntoIterator<Item = T>,
|
||||||
|
T: Into<OsString> + Clone,
|
||||||
|
{
|
||||||
|
let parsed_args = Args::parse_from(string_args);
|
||||||
|
let result = mushroom_from_args(&parsed_args);
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
Mushroom {
|
||||||
|
stalk_h,
|
||||||
|
stalk_d,
|
||||||
|
cap_h,
|
||||||
|
cap_d
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn defaults() {
|
||||||
|
validate_mushroom_from_args(["prog"], 66., 10., 34., 100.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn height_only() {
|
||||||
|
validate_mushroom_from_args(["prog", "--height", "200"], 132., 20., 68., 200.);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn height_stalk_h() {
|
||||||
|
validate_mushroom_from_args(
|
||||||
|
["prog", "--height", "200", "--stalk-h", "20"],
|
||||||
|
20.,
|
||||||
|
20.,
|
||||||
|
180.,
|
||||||
|
200.,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn height_cap_h() {
|
||||||
|
validate_mushroom_from_args(
|
||||||
|
["prog", "--height", "200", "--cap-h", "20"],
|
||||||
|
180.,
|
||||||
|
20.,
|
||||||
|
20.,
|
||||||
|
200.,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn all() {
|
||||||
|
validate_mushroom_from_args(
|
||||||
|
[
|
||||||
|
"prog",
|
||||||
|
"--height",
|
||||||
|
"10",
|
||||||
|
"--stalk-h",
|
||||||
|
"20",
|
||||||
|
"--stalk-d",
|
||||||
|
"30",
|
||||||
|
"--cap-h",
|
||||||
|
"40",
|
||||||
|
"--cap-d",
|
||||||
|
"50",
|
||||||
|
],
|
||||||
|
20.,
|
||||||
|
30.,
|
||||||
|
40.,
|
||||||
|
50.,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Mushroom {
|
||||||
|
/// Stalk height in millimeters. Zero for default.
|
||||||
|
pub stalk_h: f32,
|
||||||
|
|
||||||
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
|
pub stalk_d: f32,
|
||||||
|
|
||||||
|
/// Cap height in millimeters. Zero for default.
|
||||||
|
pub cap_h: f32,
|
||||||
|
|
||||||
|
/// Stalk diameter in millimeters. Zero for default.
|
||||||
|
pub cap_d: f32,
|
||||||
|
}
|
||||||
+233
@@ -0,0 +1,233 @@
|
|||||||
|
use stl_io::{self, Normal, Triangle, Vertex};
|
||||||
|
|
||||||
|
fn new_triangle(p1: &Vertex, p2: &Vertex, p3: &Vertex) -> stl_io::Triangle {
|
||||||
|
// Set Vector U to (Triangle.p2 minus Triangle.p1)
|
||||||
|
// Set Vector V to (Triangle.p3 minus Triangle.p1)
|
||||||
|
|
||||||
|
// Set Normal.x to (multiply U.y by V.z) minus (multiply U.z by V.y)
|
||||||
|
// Set Normal.y to (multiply U.z by V.x) minus (multiply U.x by V.z)
|
||||||
|
// Set Normal.z to (multiply U.x by V.y) minus (multiply U.y by V.x)
|
||||||
|
|
||||||
|
let u = [p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2]];
|
||||||
|
let v = [p3[0] - p1[0], p3[1] - p1[1], p3[2] - p1[2]];
|
||||||
|
|
||||||
|
let n_x = (u[1] * v[2]) - (u[2] * v[1]);
|
||||||
|
let n_y = (u[2] * v[0]) - (u[0] * v[2]);
|
||||||
|
let n_z = (u[0] * v[1]) - (u[1] * v[0]);
|
||||||
|
|
||||||
|
stl_io::Triangle {
|
||||||
|
normal: Normal::new([n_x, n_y, n_z]),
|
||||||
|
vertices: [p1.clone(), p2.clone(), p3.clone()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// N steps around a circle, measured in radians.
|
||||||
|
fn radian_steps(steps: u32) -> Vec<f32> {
|
||||||
|
let counter = 0..steps;
|
||||||
|
let stepsize = std::f64::consts::TAU / steps as f64;
|
||||||
|
|
||||||
|
return counter.map(|i| (i as f64 * stepsize) as f32).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a point on the cirle at a given angle (in radians).
|
||||||
|
fn vertex_on_circle(center: &Vertex, radius: f32, angle: f32) -> Vertex {
|
||||||
|
let x = center[0] + (radius * angle.cos());
|
||||||
|
let y = center[1] + (radius * angle.sin());
|
||||||
|
let z = center[2];
|
||||||
|
|
||||||
|
return Vertex::new([x, y, z]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vertexes_on_circle(center: &Vertex, radius: f32, steps: u32) -> Vec<Vertex> {
|
||||||
|
return radian_steps(steps)
|
||||||
|
.iter()
|
||||||
|
.map(|angle| vertex_on_circle(center, radius, *angle))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tube_base(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
||||||
|
let mut result = Vec::with_capacity(edge.len());
|
||||||
|
|
||||||
|
for i in 0..edge.len() {
|
||||||
|
result.push(new_triangle(¢er, &edge[(i + 1) % edge.len()], &edge[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tube_sides(botoom_edge: &Vec<Vertex>, top_edge: &Vec<Vertex>) -> Vec<Triangle> {
|
||||||
|
assert_eq!(botoom_edge.len(), top_edge.len());
|
||||||
|
|
||||||
|
let mut result = Vec::with_capacity(2 * botoom_edge.len());
|
||||||
|
|
||||||
|
for i in 0..botoom_edge.len() {
|
||||||
|
let j = (i + 1) % botoom_edge.len();
|
||||||
|
|
||||||
|
result.push(new_triangle(&botoom_edge[i], &top_edge[j], &top_edge[i]));
|
||||||
|
result.push(new_triangle(&botoom_edge[i], &botoom_edge[j], &top_edge[j]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tube_top(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
||||||
|
// The top is the same, except the normals are reversed. We can do that by reversing the order of the vertexes.
|
||||||
|
let mut edge = edge.clone();
|
||||||
|
edge.reverse();
|
||||||
|
|
||||||
|
return tube_base(¢er, &edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cylinder(height: f32, radius: f32, steps: u32) -> Vec<Triangle> {
|
||||||
|
let base_center = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
|
let base_edge = vertexes_on_circle(&base_center, radius, steps);
|
||||||
|
let top_center = Vertex::new([0.0, 0.0, height]);
|
||||||
|
let top_edge = vertexes_on_circle(&top_center, radius, steps);
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
result.append(&mut tube_base(&base_center, &base_edge));
|
||||||
|
result.append(&mut tube_sides(&base_edge, &top_edge));
|
||||||
|
result.append(&mut tube_top(&top_center, &top_edge));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_triangle_normal() {
|
||||||
|
// Simple triangle
|
||||||
|
let a = Vertex::new([1.0, 0.0, 0.0]);
|
||||||
|
let b = Vertex::new([0.0, 1.0, 0.0]);
|
||||||
|
let c = Vertex::new([-1.0, 0.0, 0.0]);
|
||||||
|
|
||||||
|
let tri = new_triangle(&a, &b, &c);
|
||||||
|
|
||||||
|
assert_eq!(tri.normal, Normal::new([0.0, 0.0, 2.0]));
|
||||||
|
|
||||||
|
// Same triangle, reverse order.
|
||||||
|
let tri = new_triangle(&c, &b, &a);
|
||||||
|
|
||||||
|
assert_eq!(tri.normal, Normal::new([0.0, 0.0, -2.0]));
|
||||||
|
|
||||||
|
// Uneven numbers.
|
||||||
|
let a = Vertex::new([2.0, 0.0, 0.0]);
|
||||||
|
let b = Vertex::new([0.0, 3.0, 0.0]);
|
||||||
|
let c = Vertex::new([-4.0, 0.0, 0.0]);
|
||||||
|
|
||||||
|
let tri_big = new_triangle(&a, &b, &c);
|
||||||
|
|
||||||
|
assert_eq!(tri_big.normal, Normal::new([0.0, 0.0, 18.0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn radian_steps_basic() {
|
||||||
|
assert_eq!(radian_steps(0), []);
|
||||||
|
assert_eq!(radian_steps(1), [0.0]);
|
||||||
|
assert_eq!(radian_steps(2), [0.0, std::f32::consts::PI]);
|
||||||
|
assert_eq!(
|
||||||
|
radian_steps(3),
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
std::f32::consts::PI * 2.0 / 3.0,
|
||||||
|
std::f32::consts::PI * 4.0 / 3.0
|
||||||
|
]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
radian_steps(4),
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
std::f32::consts::PI * 0.5,
|
||||||
|
std::f32::consts::PI,
|
||||||
|
std::f32::consts::PI * 1.5,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_vec_vertex_relative_eq(val: &Vec<Vertex>, expected: &Vec<Vertex>) {
|
||||||
|
let mut pass = true;
|
||||||
|
|
||||||
|
if val.len() != expected.len() {
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..val.len() {
|
||||||
|
for j in 0..3 {
|
||||||
|
if !relative_eq!(val[i][j], expected[i][j]) {
|
||||||
|
pass = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pass {
|
||||||
|
// We re-compare here (which will always fail) to get the friendly error message,
|
||||||
|
// which I can't figure out how to generate directly.
|
||||||
|
assert_eq!(val, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vertexes_on_circle_basic() {
|
||||||
|
let val = vertexes_on_circle(&Vertex::new([0.0, 0.0, 0.0]), 1.0, 4);
|
||||||
|
|
||||||
|
let expected = [
|
||||||
|
Vertex::new([1.0, 0.0, 0.0]),
|
||||||
|
Vertex::new([0.0, 1.0, 0.0]),
|
||||||
|
Vertex::new([-1.0, 0.0, 0.0]),
|
||||||
|
Vertex::new([0.0, -1.0, 0.0]),
|
||||||
|
]
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
validate_vec_vertex_relative_eq(&val, &expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vertexes_on_circle_complex() {
|
||||||
|
let val = vertexes_on_circle(&Vertex::new([4.0, 5.0, 6.0]), 3.0, 4);
|
||||||
|
|
||||||
|
let expected = [
|
||||||
|
Vertex::new([7.0, 5.0, 6.0]),
|
||||||
|
Vertex::new([4.0, 8.0, 6.0]),
|
||||||
|
Vertex::new([1.0, 5.0, 6.0]),
|
||||||
|
Vertex::new([4.0, 2.0, 6.0]),
|
||||||
|
]
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
validate_vec_vertex_relative_eq(&val, &expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tube_base_test() {
|
||||||
|
// fn tube_base(center: &Vertex, edge: &Vec<Vertex>) -> Vec<Triangle> {
|
||||||
|
|
||||||
|
let center = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
|
let edge = vertexes_on_circle(¢er, 1.0, 4);
|
||||||
|
|
||||||
|
let triangles = tube_base(¢er, &edge);
|
||||||
|
|
||||||
|
assert_eq!(triangles.len(), edge.len());
|
||||||
|
|
||||||
|
for t in triangles {
|
||||||
|
// All of these triangles should have the same normal (down).
|
||||||
|
assert_eq!(t.normal, Vertex::new([0.0, 0.0, -1.0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tube_top_test() {
|
||||||
|
let center = Vertex::new([0.0, 0.0, 0.0]);
|
||||||
|
let edge = vertexes_on_circle(¢er, 1.0, 4);
|
||||||
|
|
||||||
|
let triangles = tube_top(¢er, &edge);
|
||||||
|
|
||||||
|
assert_eq!(triangles.len(), edge.len());
|
||||||
|
|
||||||
|
for t in triangles {
|
||||||
|
// All of these triangles should have the same normal (up).
|
||||||
|
assert_eq!(t.normal, Vertex::new([0.0, 0.0, 1.0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user