Mesh builder and line support generation
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
use egui::{Context, Ui};
|
use egui::{Context, Ui};
|
||||||
use slicer::supports::line::LineSupportGenerator;
|
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) {
|
pub fn ui(app: &mut App, ui: &mut Ui, _ctx: &Context) {
|
||||||
if ui.button("Generate").clicked() {
|
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,
|
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);
|
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;
|
use nalgebra::Vector3;
|
||||||
|
|
||||||
|
pub mod builder;
|
||||||
pub mod half_edge;
|
pub mod half_edge;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod segments;
|
pub mod segments;
|
||||||
|
@@ -35,7 +35,13 @@ impl Mesh {
|
|||||||
/// transformations are all 0 by default.
|
/// transformations are all 0 by default.
|
||||||
pub fn new(mut vertices: Vec<Pos>, faces: Vec<[u32; 3]>, normals: Vec<Pos>) -> Self {
|
pub fn new(mut vertices: Vec<Pos>, faces: Vec<[u32; 3]>, normals: Vec<Pos>) -> Self {
|
||||||
center_vertices(&mut vertices);
|
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 {
|
Self {
|
||||||
inner: Arc::new(MeshInner {
|
inner: Arc::new(MeshInner {
|
||||||
vertices: vertices.into_boxed_slice(),
|
vertices: vertices.into_boxed_slice(),
|
||||||
|
@@ -4,7 +4,7 @@ use nalgebra::{Vector2, Vector3};
|
|||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{half_edge::HalfEdgeMesh, mesh::Mesh};
|
use crate::{builder::MeshBuilder, half_edge::HalfEdgeMesh, mesh::Mesh};
|
||||||
|
|
||||||
pub struct LineSupportGenerator<'a> {
|
pub struct LineSupportGenerator<'a> {
|
||||||
config: &'a LineSupportConfig,
|
config: &'a LineSupportConfig,
|
||||||
@@ -18,6 +18,11 @@ pub struct LineSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct LineSupportConfig {
|
pub struct LineSupportConfig {
|
||||||
|
/// Support generation
|
||||||
|
pub support_radius: f32,
|
||||||
|
pub base_radius: f32,
|
||||||
|
|
||||||
|
/// Overhang detection
|
||||||
pub max_origin_normal_z: f32,
|
pub max_origin_normal_z: f32,
|
||||||
pub max_neighbor_z_diff: f32,
|
pub max_neighbor_z_diff: f32,
|
||||||
pub min_angle: f32,
|
pub min_angle: f32,
|
||||||
@@ -29,16 +34,33 @@ impl<'a> LineSupportGenerator<'a> {
|
|||||||
Self { config, bed_size }
|
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 half_edge_mesh = HalfEdgeMesh::new(mesh);
|
||||||
|
|
||||||
let mut points = Vec::new();
|
let mut overhangs = Vec::new();
|
||||||
points.extend_from_slice(&self.detect_point_overhangs(mesh, &half_edge_mesh));
|
|
||||||
points.extend_from_slice(&self.detect_face_overhangs(mesh));
|
|
||||||
|
|
||||||
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
|
/// 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 {
|
impl Default for LineSupportConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
support_radius: 0.1,
|
||||||
|
base_radius: 1.0,
|
||||||
|
|
||||||
max_origin_normal_z: 0.0,
|
max_origin_normal_z: 0.0,
|
||||||
max_neighbor_z_diff: -0.01,
|
max_neighbor_z_diff: -0.01,
|
||||||
min_angle: 3.0,
|
min_angle: 3.0,
|
||||||
|
Reference in New Issue
Block a user