Init project
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/output
|
1251
Cargo.lock
generated
Normal file
1251
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "sla_slicer"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
image = "0.25.1"
|
||||||
|
imageproc = "0.25.0"
|
||||||
|
nalgebra = "0.32.6"
|
||||||
|
stl_io = "0.7.0"
|
130
src/main.rs
Normal file
130
src/main.rs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Read, Seek},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use image::RgbImage;
|
||||||
|
use nalgebra::Vector3;
|
||||||
|
|
||||||
|
type Pos = Vector3<f32>;
|
||||||
|
|
||||||
|
struct Mesh {
|
||||||
|
vertices: Vec<Pos>,
|
||||||
|
faces: Vec<[usize; 3]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
const FILE_PATH: &str = "teapot.stl";
|
||||||
|
|
||||||
|
let mut file = File::open(FILE_PATH)?;
|
||||||
|
let mesh = load_mesh(&mut file, "stl")?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Loaded mesh. {{ vert: {}, face: {} }}",
|
||||||
|
mesh.vertices.len(),
|
||||||
|
mesh.faces.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let (min, max) = mesh.minmax_point();
|
||||||
|
let slice_height = 0.1;
|
||||||
|
|
||||||
|
let mut height = 0.0;
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
|
while height < max.z {
|
||||||
|
let plane_normal = Pos::new(0.0, 0.0, 1.0);
|
||||||
|
let plane_point = Pos::new(0.0, 0.0, height);
|
||||||
|
|
||||||
|
let intersections = mesh.intersect_plane(&plane_normal, &plane_point);
|
||||||
|
println!("Height: {}, Intersections: {}", height, intersections.len());
|
||||||
|
|
||||||
|
let mut image = RgbImage::new(1920, 1080);
|
||||||
|
for intersection in intersections.chunks(2) {
|
||||||
|
let x1 = ((intersection[0].x - min.x) / (max.x - min.x)) * image.width() as f32;
|
||||||
|
let y1 = ((intersection[0].y - min.y) / (max.y - min.y)) * image.height() as f32;
|
||||||
|
|
||||||
|
let x2 = ((intersection[1].x - min.x) / (max.x - min.x)) * image.width() as f32;
|
||||||
|
let y2 = ((intersection[1].y - min.y) / (max.y - min.y)) * image.height() as f32;
|
||||||
|
|
||||||
|
imageproc::drawing::draw_line_segment_mut(
|
||||||
|
&mut image,
|
||||||
|
(x1 as f32, y1 as f32),
|
||||||
|
(x2 as f32, y2 as f32),
|
||||||
|
image::Rgb([255, 255, 255]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = format!("output/{i}.png");
|
||||||
|
image.save(filename)?;
|
||||||
|
|
||||||
|
height += slice_height;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mesh {
|
||||||
|
pub fn minmax_point(&self) -> (Pos, Pos) {
|
||||||
|
self.vertices.iter().fold(
|
||||||
|
(
|
||||||
|
Pos::new(f32::MAX, f32::MAX, f32::MAX),
|
||||||
|
Pos::new(f32::MIN, f32::MIN, f32::MIN),
|
||||||
|
),
|
||||||
|
|(min, max), v| {
|
||||||
|
(
|
||||||
|
Pos::new(min.x.min(v.x), min.y.min(v.y), min.z.min(v.z)),
|
||||||
|
Pos::new(max.x.max(v.x), max.y.max(v.y), max.z.max(v.z)),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersect_plane(&self, normal: &Pos, point: &Pos) -> Vec<Pos> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for face in &self.faces {
|
||||||
|
for (v0, v1) in [
|
||||||
|
(self.vertices[face[0]], self.vertices[face[1]]),
|
||||||
|
(self.vertices[face[1]], self.vertices[face[2]]),
|
||||||
|
(self.vertices[face[2]], self.vertices[face[0]]),
|
||||||
|
] {
|
||||||
|
let d0 = v0 - point;
|
||||||
|
let d1 = v1 - point;
|
||||||
|
|
||||||
|
let dot0 = normal.dot(&d0);
|
||||||
|
let dot1 = normal.dot(&d1);
|
||||||
|
|
||||||
|
if dot0 * dot1 < 0.0 {
|
||||||
|
let t = dot0 / (dot0 - dot1);
|
||||||
|
let intersection = v0 + t * (v1 - v0);
|
||||||
|
result.push(intersection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_mesh<T: Read + Seek>(reader: &mut T, format: &str) -> Result<Mesh> {
|
||||||
|
match format {
|
||||||
|
"stl" => {
|
||||||
|
let modal = stl_io::read_stl(reader)?;
|
||||||
|
Ok(Mesh {
|
||||||
|
vertices: modal
|
||||||
|
.vertices
|
||||||
|
.iter()
|
||||||
|
.map(|v| Pos::new(v[0], v[1], v[2]))
|
||||||
|
.collect(),
|
||||||
|
faces: modal
|
||||||
|
.faces
|
||||||
|
.iter()
|
||||||
|
.map(|f| [f.vertices[0], f.vertices[1], f.vertices[2]])
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(anyhow::anyhow!("Unsupported format: {}", format)),
|
||||||
|
}
|
||||||
|
}
|
BIN
teapot.stl
Normal file
BIN
teapot.stl
Normal file
Binary file not shown.
Reference in New Issue
Block a user