Mesh builder and line support generation

This commit is contained in:
Connor Slade
2024-08-07 19:00:29 -04:00
parent 3f7a8fe9bf
commit 2e818d4f4f
5 changed files with 158 additions and 10 deletions

View File

@@ -1,7 +1,7 @@
use egui::{Context, Ui};
use slicer::supports::line::LineSupportGenerator;
use crate::{app::App, ui::components::dragger};
use crate::{app::App, render::rendered_mesh::RenderedMesh, ui::components::dragger};
pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
if ui.button("Generate").clicked() {
@@ -11,9 +11,17 @@ pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
app.slice_config.platform_size,
);
for mesh in app.meshes.read().iter() {
let mut meshes = app.meshes.write();
for i in 0..meshes.len() {
let mesh = &meshes[i];
let supports = generator.generate_line_supports(&mesh.mesh);
app.state.line_support_debug.extend(supports);
let mesh = RenderedMesh::from_mesh(supports)
.with_name("Supports".into())
.with_random_color();
meshes.push(mesh);
}
}

108
slicer/src/builder.rs Normal file
View File

@@ -0,0 +1,108 @@
use std::f32::consts::PI;
use anyhow::Result;
use nalgebra::Vector3;
use stl_io::Triangle;
use crate::mesh::Mesh;
pub struct MeshBuilder {
vertices: Vec<Vector3<f32>>,
normals: Vec<Vector3<f32>>,
faces: Vec<[u32; 3]>,
}
impl MeshBuilder {
pub fn new() -> Self {
Self {
vertices: Vec::new(),
normals: Vec::new(),
faces: Vec::new(),
}
}
pub fn add_vertex(&mut self, vertex: Vector3<f32>) -> u32 {
self.vertices.push(vertex);
(self.vertices.len() - 1) as u32
}
pub fn add_face(&mut self, face: [u32; 3], normal: Vector3<f32>) {
self.faces.push(face);
self.normals.push(normal);
}
pub fn add_quad(&mut self, quad: [u32; 4], normal: Vector3<f32>) {
self.add_face([quad[0], quad[1], quad[2]], normal);
self.add_face([quad[1], quad[2], quad[3]], normal);
}
pub fn build(self) -> Mesh {
Mesh::new(self.vertices, self.faces, self.normals)
}
pub fn save_stl(&self, path: &str) -> Result<()> {
let mut stl = Vec::new();
for (face, normal) in self.faces.iter().zip(self.normals.iter()) {
let (a, b, c) = (
self.vertices[face[0] as usize],
self.vertices[face[1] as usize],
self.vertices[face[2] as usize],
);
let into_stl = |v: Vector3<f32>| stl_io::Vertex::new([v.x, v.y, v.z]);
stl.push(Triangle {
normal: into_stl(*normal),
vertices: [into_stl(a), into_stl(b), into_stl(c)],
});
}
let mut file = std::fs::File::create(path)?;
stl_io::write_stl(&mut file, stl.iter())?;
Ok(())
}
}
impl MeshBuilder {
pub fn add_vertical_cylinder(
&mut self,
bottom: Vector3<f32>,
height: f32,
radius: f32,
precision: u32,
) {
let top = bottom + Vector3::new(0.0, 0.0, height);
let bottom_center = self.add_vertex(bottom);
let top_center = self.add_vertex(top);
let mut last = None;
let mut fist = None;
for i in 0..precision {
let angle = 2.0 * PI * (i as f32) / (precision as f32);
let normal = Vector3::new(angle.cos(), angle.sin(), 0.0);
let offset = normal * radius;
let top = self.add_vertex(top + offset);
let bottom = self.add_vertex(bottom + offset);
if let Some((last_top, last_bottom)) = last {
self.add_quad([last_top, last_bottom, top, bottom], normal);
self.add_face([last_top, top, top_center], Vector3::z());
self.add_face([last_bottom, bottom_center, bottom], -Vector3::z());
}
last = Some((top, bottom));
if fist.is_none() {
fist = Some((top, bottom, normal));
}
}
if let Some((first_top, first_bottom, normal)) = fist {
if let Some((last_top, last_bottom)) = last {
self.add_quad([last_top, last_bottom, first_top, first_bottom], normal);
self.add_face([last_top, first_top, top_center], Vector3::z());
self.add_face([last_bottom, bottom_center, first_bottom], -Vector3::z());
}
}
}
}

View File

@@ -2,6 +2,7 @@
use nalgebra::Vector3;
pub mod builder;
pub mod half_edge;
pub mod mesh;
pub mod segments;

View File

@@ -35,7 +35,13 @@ impl Mesh {
/// transformations are all 0 by default.
pub fn new(mut vertices: Vec<Pos>, faces: Vec<[u32; 3]>, normals: Vec<Pos>) -> Self {
center_vertices(&mut vertices);
Self::new_uncentred(vertices, faces, normals)
}
/// Creates a new mesh from the givin vertices and faces. The
/// transformations are all 0 by default and the vertices are
/// not centered.
pub fn new_uncentred(vertices: Vec<Pos>, faces: Vec<[u32; 3]>, normals: Vec<Pos>) -> Self {
Self {
inner: Arc::new(MeshInner {
vertices: vertices.into_boxed_slice(),

View File

@@ -4,7 +4,7 @@ use nalgebra::{Vector2, Vector3};
use ordered_float::OrderedFloat;
use tracing::info;
use crate::{half_edge::HalfEdgeMesh, mesh::Mesh};
use crate::{builder::MeshBuilder, half_edge::HalfEdgeMesh, mesh::Mesh};
pub struct LineSupportGenerator<'a> {
config: &'a LineSupportConfig,
@@ -18,6 +18,11 @@ pub struct LineSupport {
}
pub struct LineSupportConfig {
/// Support generation
pub support_radius: f32,
pub base_radius: f32,
/// Overhang detection
pub max_origin_normal_z: f32,
pub max_neighbor_z_diff: f32,
pub min_angle: f32,
@@ -29,16 +34,33 @@ impl<'a> LineSupportGenerator<'a> {
Self { config, bed_size }
}
pub fn generate_line_supports(&self, mesh: &Mesh) -> Vec<[Vector3<f32>; 2]> {
pub fn generate_line_supports(&self, mesh: &Mesh) -> Mesh {
let half_edge_mesh = HalfEdgeMesh::new(mesh);
let mut points = Vec::new();
points.extend_from_slice(&self.detect_point_overhangs(mesh, &half_edge_mesh));
points.extend_from_slice(&self.detect_face_overhangs(mesh));
let mut overhangs = Vec::new();
info!("Line support generation identified {} points", points.len());
let point_overhangs = self.detect_point_overhangs(mesh, &half_edge_mesh);
overhangs.extend_from_slice(&point_overhangs);
points
let face_overhangs = self.detect_face_overhangs(mesh);
overhangs.extend_from_slice(&face_overhangs);
info!(
"Found {} overhangs {{ face: {}, point: {} }}",
overhangs.len(),
face_overhangs.len(),
point_overhangs.len()
);
let mut out = MeshBuilder::new();
for [origin, _normal] in overhangs {
let bottom = origin.xy().to_homogeneous();
out.add_vertical_cylinder(bottom, origin.z, self.config.support_radius, 10);
}
out.save_stl("debug.stl").unwrap();
out.build()
}
/// Find all points that are both lower than their surrounding points and have down facing normals
@@ -183,6 +205,9 @@ fn ray_triangle_intersection(
impl Default for LineSupportConfig {
fn default() -> Self {
Self {
support_radius: 0.1,
base_radius: 1.0,
max_origin_normal_z: 0.0,
max_neighbor_z_diff: -0.01,
min_angle: 3.0,