Mesh builder and line support generation
This commit is contained in:
@@ -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
108
slicer/src/builder.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
|
||||
use nalgebra::Vector3;
|
||||
|
||||
pub mod builder;
|
||||
pub mod half_edge;
|
||||
pub mod mesh;
|
||||
pub mod segments;
|
||||
|
@@ -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(),
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user