Working slicer

This commit is contained in:
Connor Slade
2024-06-19 15:31:57 -04:00
parent f5710b45e7
commit b55f2f8ac1
7 changed files with 105 additions and 224 deletions

11
Cargo.lock generated
View File

@@ -790,6 +790,15 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "ordered-float"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "owned_ttf_parser" name = "owned_ttf_parser"
version = "0.21.0" version = "0.21.0"
@@ -1089,6 +1098,8 @@ dependencies = [
"image", "image",
"imageproc", "imageproc",
"nalgebra", "nalgebra",
"ordered-float",
"rand",
"rayon", "rayon",
"stl_io", "stl_io",
] ]

View File

@@ -3,5 +3,5 @@ mod serializer;
mod types; mod types;
pub use deserializer::Deserializer; pub use deserializer::Deserializer;
pub use serializer::Serializer; pub use serializer::{DynamicSerializer, Serializer, SizedSerializer};
pub use types::SizedString; pub use types::SizedString;

View File

@@ -93,6 +93,11 @@ impl LayerEncoder {
self.last_value = value; self.last_value = value;
} }
pub fn finish(self) -> (Vec<u8>, u8) {
let checksum = calculate_checksum(&self.data);
(self.data, checksum)
}
} }
impl<'a> LayerDecoder<'a> { impl<'a> LayerDecoder<'a> {

View File

@@ -45,6 +45,7 @@ impl LayerContent {
ser.write_f32(self.second_retract_distance); ser.write_f32(self.second_retract_distance);
ser.write_f32(self.second_retract_speed); ser.write_f32(self.second_retract_speed);
ser.write_u16(self.light_pwm); ser.write_u16(self.light_pwm);
ser.write_bytes(DELIMITER);
ser.write_u32(self.data.len() as u32 + 2); ser.write_u32(self.data.len() as u32 + 2);
ser.write_bytes(&[0x55]); ser.write_bytes(&[0x55]);
ser.write_bytes(&self.data); ser.write_bytes(&self.data);

View File

@@ -10,6 +10,8 @@ anyhow = "1.0.86"
image = "0.25.1" image = "0.25.1"
imageproc = "0.25.0" imageproc = "0.25.0"
nalgebra = "0.32.6" nalgebra = "0.32.6"
ordered-float = "4.2.0"
rand = "0.8.5"
rayon = "1.10.0" rayon = "1.10.0"
stl_io = "0.7.0" stl_io = "0.7.0"

View File

@@ -1,16 +1,20 @@
use std::{fs::File, time::Instant}; use std::{
fs::{self, File},
time::Instant,
};
use anyhow::Result; use anyhow::Result;
use image::RgbImage; use common::serde::DynamicSerializer;
use imageproc::point::Point; use goo_format::{File as GooFile, HeaderInfo, LayerContent, LayerEncoder};
use image::{Rgb, RgbImage};
use mesh::load_mesh; use mesh::load_mesh;
use nalgebra::{Vector2, Vector3}; use nalgebra::{Vector2, Vector3};
use tmp_image::{draw_line_segment_invert_mut, draw_polygon_with_mut}; use ordered_float::OrderedFloat;
use rayon::iter::{ParallelBridge, ParallelIterator};
type Pos = Vector3<f32>; type Pos = Vector3<f32>;
mod mesh; mod mesh;
mod tmp_image;
struct SliceConfig { struct SliceConfig {
platform_resolution: Vector2<u32>, platform_resolution: Vector2<u32>,
@@ -20,6 +24,7 @@ struct SliceConfig {
fn main() -> Result<()> { fn main() -> Result<()> {
const FILE_PATH: &str = "teapot.stl"; const FILE_PATH: &str = "teapot.stl";
const OUTPUT_PATH: &str = "output.goo";
let slice_config = SliceConfig { let slice_config = SliceConfig {
// platform_resolution: Vector2::new(1920, 1080), // platform_resolution: Vector2::new(1920, 1080),
@@ -32,8 +37,12 @@ fn main() -> Result<()> {
let mut mesh = load_mesh(&mut file, "stl")?; let mut mesh = load_mesh(&mut file, "stl")?;
let (min, max) = mesh.minmax_point(); let (min, max) = mesh.minmax_point();
let real_scale = 100.0; let real_scale = 1.0;
mesh.scale = Pos::new(real_scale, real_scale, 1.0); mesh.scale = Pos::new(
real_scale / slice_config.platform_size.x * slice_config.platform_resolution.x as f32,
real_scale / slice_config.platform_size.y * slice_config.platform_resolution.y as f32,
real_scale,
);
let center = slice_config.platform_resolution / 2; let center = slice_config.platform_resolution / 2;
let mesh_center = (min + max) / 2.0; let mesh_center = (min + max) / 2.0;
@@ -51,104 +60,83 @@ fn main() -> Result<()> {
let now = Instant::now(); let now = Instant::now();
let mut height = 0.0;
let mut i = 0;
let mut image = RgbImage::new(
slice_config.platform_resolution.x,
slice_config.platform_resolution.y,
);
let max = mesh.transform(&max); let max = mesh.transform(&max);
while height < max.z { let layers = (max.z / slice_config.slice_height).ceil() as u32;
let intersections = mesh.intersect_plane(height);
println!("Height: {}, Intersections: {}", height, intersections.len());
let mut segments = intersections let layers = (0..layers)
.chunks(2) .par_bridge()
.map(|x| (x[0], x[1])) .map(|layer| {
.collect::<Vec<_>>(); let height = layer as f32 * slice_config.slice_height;
if segments.is_empty() { let intersections = mesh.intersect_plane(height);
height += slice_config.slice_height; println!("Height: {}, Intersections: {}", height, intersections.len());
i += 1;
continue;
}
fn points_equal(a: &Pos, b: &Pos) -> bool { let segments = intersections
(a.x - b.x).abs() < 0.0001 && (a.y - b.y).abs() < 0.0001 .chunks(2)
} .map(|x| (x[0], x[1]))
fn points_equal_int(a: &Pos, b: &Pos) -> bool {
(a.x as i32 == b.x as i32) && (a.y as i32 == b.y as i32)
}
let mut polygons = Vec::new();
let mut polygon = Vec::new();
'outer: loop {
if polygon.is_empty() {
if segments.is_empty() {
break;
}
let first = segments.remove(0);
polygon.push(first.0);
polygon.push(first.1);
}
for j in 0..segments.len() {
let (a, b) = segments[j];
let last = polygon.last().unwrap();
if points_equal(&last, &a) {
polygon.push(b);
segments.remove(j);
continue 'outer;
} else if points_equal(&last, &b) {
polygon.push(a);
segments.remove(j);
continue 'outer;
}
}
polygons.push(polygon.clone());
polygon.clear();
}
for mut polygon in polygons {
while !polygon.is_empty() && points_equal(&polygon[0], polygon.last().unwrap()) {
polygon.pop();
}
while points_equal_int(&polygon[0], polygon.last().unwrap()) {
polygon[0].x -= 1.0;
}
if polygon.len() < 3 {
continue;
}
let polygons = polygon
.into_iter()
.map(|x| Point::new(x.x as i32, x.y as i32))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
draw_polygon_with_mut( let mut out = Vec::new();
&mut image, for y in 0..slice_config.platform_resolution.y {
&polygons, let mut intersections = segments
image::Rgb([255, 255, 255]), .iter()
draw_line_segment_invert_mut, .filter_map(|(a, b)| {
); let y = y as f32;
} if a.y <= y && b.y >= y {
let t = (y - a.y) / (b.y - a.y);
let x = a.x + t * (b.x - a.x);
Some(x)
} else if b.y <= y && a.y >= y {
let t = (y - b.y) / (a.y - b.y);
let x = b.x + t * (a.x - b.x);
Some(x)
} else {
None
}
})
.collect::<Vec<_>>();
let filename = format!("slice_output/{i}.png"); intersections.sort_by_key(|&x| OrderedFloat(x));
image.save(filename)?; intersections.dedup();
image.fill(0);
height += slice_config.slice_height; for span in intersections.chunks_exact(2) {
i += 1; out.push((span[0] as u64, span[1] as u64));
} }
}
let mut encoder = LayerEncoder::new();
let mut last = 0;
for (start, end) in out {
if start > last {
encoder.add_run(start - last, 0);
}
encoder.add_run(end - start, 1);
last = end;
}
let (data, checksum) = encoder.finish();
println!("#{layer} Data Size: {}", data.len());
LayerContent {
data,
checksum,
..Default::default()
}
})
.collect::<Vec<_>>();
let goo = GooFile::new(
HeaderInfo {
layer_count: layers.len() as u32,
..Default::default()
},
layers,
);
let mut serializer = DynamicSerializer::new();
goo.serialize(&mut serializer);
fs::write(OUTPUT_PATH, serializer.into_inner())?;
println!("Done. Elapsed: {:.1}s", now.elapsed().as_secs_f32()); println!("Done. Elapsed: {:.1}s", now.elapsed().as_secs_f32());

View File

@@ -1,126 +0,0 @@
use std::cmp::{max, min};
use image::{ImageBuffer, Pixel, Rgb};
use imageproc::{
drawing::{BresenhamLineIter, Canvas},
point::Point,
};
pub fn draw_polygon_with_mut<L>(
canvas: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
poly: &[Point<i32>],
color: Rgb<u8>,
plotter: L,
) where
L: Fn(&mut ImageBuffer<Rgb<u8>, Vec<u8>>, (f32, f32), (f32, f32), Rgb<u8>),
{
if poly.is_empty() {
return;
}
if poly[0] == poly[poly.len() - 1] {
panic!(
"First point {:?} == last point {:?}",
poly[0],
poly[poly.len() - 1]
);
}
let mut y_min = i32::MAX;
let mut y_max = i32::MIN;
for p in poly {
y_min = min(y_min, p.y);
y_max = max(y_max, p.y);
}
let (width, height) = canvas.dimensions();
// Intersect polygon vertical range with image bounds
y_min = max(0, min(y_min, height as i32 - 1));
y_max = max(0, min(y_max, height as i32 - 1));
let mut closed: Vec<Point<i32>> = poly.to_vec();
closed.push(poly[0]);
let edges: Vec<&[Point<i32>]> = closed.windows(2).collect();
let mut intersections = Vec::new();
for y in y_min..y_max + 1 {
for edge in &edges {
let p0 = edge[0];
let p1 = edge[1];
if p0.y <= y && p1.y >= y || p1.y <= y && p0.y >= y {
if p0.y == p1.y {
// Need to handle horizontal lines specially
intersections.push(p0.x);
intersections.push(p1.x);
} else if p0.y == y || p1.y == y {
if p1.y > y {
intersections.push(p0.x);
}
if p0.y > y {
intersections.push(p1.x);
}
} else {
let fraction = (y - p0.y) as f32 / (p1.y - p0.y) as f32;
let inter = p0.x as f32 + fraction * (p1.x - p0.x) as f32;
intersections.push(inter.round() as i32);
}
}
}
intersections.sort_unstable();
intersections.chunks(2).for_each(|range| {
let mut from = min(range[0], width as i32);
let mut to = min(range[1], width as i32 - 1);
if from < width as i32 && to >= 0 {
// draw only if range appears on the canvas
from = max(0, from);
to = max(0, to);
for x in from..to + 1 {
let current = canvas.get_pixel(x as u32, y as u32);
if *current == color {
canvas.draw_pixel(x as u32, y as u32, Rgb([0, 0, 0]));
} else {
canvas.draw_pixel(x as u32, y as u32, color);
}
}
}
});
intersections.clear();
}
for edge in &edges {
let start = (edge[0].x as f32, edge[0].y as f32);
let end = (edge[1].x as f32, edge[1].y as f32);
plotter(canvas, start, end, color);
}
}
pub fn draw_line_segment_invert_mut(
canvas: &mut ImageBuffer<Rgb<u8>, Vec<u8>>,
start: (f32, f32),
end: (f32, f32),
color: Rgb<u8>,
) {
let (width, height) = canvas.dimensions();
let in_bounds = |x, y| x >= 0 && x < width as i32 && y >= 0 && y < height as i32;
let line_iterator = BresenhamLineIter::new(start, end);
for point in line_iterator {
let x = point.0;
let y = point.1;
if in_bounds(x, y) {
let current = canvas.get_pixel(x as u32, y as u32);
if *current == color {
canvas.draw_pixel(x as u32, y as u32, Rgb([0, 0, 0]));
} else {
canvas.draw_pixel(x as u32, y as u32, color);
}
}
}
}