Multithread slicer
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -931,6 +931,12 @@ dependencies = [
|
||||
"error-code",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clone-macro"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11b52aa11e0a83701c2f8a2b683c254889d8a957e7f58a5a06c5f98ebb4fc3c1"
|
||||
|
||||
[[package]]
|
||||
name = "cocoa"
|
||||
version = "0.25.0"
|
||||
@@ -3487,6 +3493,7 @@ dependencies = [
|
||||
"imageproc",
|
||||
"nalgebra",
|
||||
"ordered-float",
|
||||
"rayon",
|
||||
"stl_io",
|
||||
]
|
||||
|
||||
@@ -3869,6 +3876,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytemuck",
|
||||
"clone-macro",
|
||||
"common",
|
||||
"eframe",
|
||||
"egui",
|
||||
|
6
TODO.md
6
TODO.md
@@ -6,10 +6,14 @@
|
||||
- [ ] Allow slicing multiple modals at once
|
||||
- [ ] Fix translation in slicer (its currently moved by pixels not mm)
|
||||
- [ ] Allow rotating modals
|
||||
- [ ] Optimize slicer (BVH?)
|
||||
- [x] Optimize slicer (BVH?)
|
||||
- [x] ehh just throw rayon on it
|
||||
- [ ] Allow saving / loading projects
|
||||
- [ ] Allow deleting objects
|
||||
- [ ] Use instancing both for object mesh storage and rendering
|
||||
- [ ] Less internal dependence on GOO format
|
||||
- [ ] Anti-aliasing
|
||||
- [ ] Cache transformed points
|
||||
- [ ] Rename `ui` module to `mslicer`
|
||||
- [ ] Proper slice preview scaling
|
||||
- [ ] Preview image generation
|
||||
|
@@ -14,3 +14,4 @@ stl_io = "0.7.0"
|
||||
|
||||
common = { path = "../common" }
|
||||
goo_format = { path = "../goo_format" }
|
||||
rayon = "1.10.0"
|
||||
|
33
slicer/src/config.rs
Normal file
33
slicer/src/config.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use nalgebra::{Vector2, Vector3};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SliceConfig {
|
||||
pub platform_resolution: Vector2<u32>,
|
||||
pub platform_size: Vector3<f32>,
|
||||
pub slice_height: f32,
|
||||
|
||||
pub exposure_config: ExposureConfig,
|
||||
pub first_exposure_config: ExposureConfig,
|
||||
pub first_layers: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExposureConfig {
|
||||
pub exposure_time: f32,
|
||||
pub lift_distance: f32,
|
||||
pub lift_speed: f32,
|
||||
pub retract_distance: f32,
|
||||
pub retract_speed: f32,
|
||||
}
|
||||
|
||||
impl Default for ExposureConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
exposure_time: 3.0,
|
||||
lift_distance: 5.0,
|
||||
lift_speed: 65.0,
|
||||
retract_distance: 5.0,
|
||||
retract_speed: 150.0,
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
use nalgebra::Vector3;
|
||||
|
||||
pub mod config;
|
||||
pub mod mesh;
|
||||
pub mod slicer;
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{stdout, Write},
|
||||
thread,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
@@ -8,8 +10,9 @@ use common::serde::DynamicSerializer;
|
||||
use nalgebra::{Vector2, Vector3};
|
||||
|
||||
use slicer::{
|
||||
config::{ExposureConfig, SliceConfig},
|
||||
mesh::load_mesh,
|
||||
slicer::{slice_goo, ExposureConfig, SliceConfig},
|
||||
slicer::Slicer,
|
||||
Pos,
|
||||
};
|
||||
|
||||
@@ -60,16 +63,25 @@ fn main() -> Result<()> {
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let goo = slice_goo(&slice_config, &mesh, |layer, layers| {
|
||||
let slicer = Slicer::new(slice_config.clone(), mesh);
|
||||
let progress = slicer.progress();
|
||||
|
||||
let goo = thread::spawn(move || slicer.slice());
|
||||
|
||||
let mut completed = 0;
|
||||
while completed < progress.total() {
|
||||
completed = progress.wait();
|
||||
print!(
|
||||
"\rLayer: {}/{layers} ({:.1}%)",
|
||||
layer + 1,
|
||||
(layer as f32 + 1.0) / layers as f32 * 100.0
|
||||
"\rLayer: {}/{}, {:.1}%",
|
||||
completed,
|
||||
progress.total(),
|
||||
completed as f32 / progress.total() as f32 * 100.0
|
||||
);
|
||||
});
|
||||
stdout().flush()?;
|
||||
}
|
||||
|
||||
let mut serializer = DynamicSerializer::new();
|
||||
goo.serialize(&mut serializer);
|
||||
goo.join().unwrap().serialize(&mut serializer);
|
||||
fs::write(OUTPUT_PATH, serializer.into_inner())?;
|
||||
|
||||
println!("\nDone. Elapsed: {:.1}s", now.elapsed().as_secs_f32());
|
||||
|
@@ -50,35 +50,33 @@ impl Mesh {
|
||||
let v1 = self.transform(&self.vertices[face[1] as usize]);
|
||||
let v2 = self.transform(&self.vertices[face[2] as usize]);
|
||||
|
||||
let mut dot0 = v0.z - height;
|
||||
let mut dot1 = v1.z - height;
|
||||
let mut dot2 = v2.z - height;
|
||||
let a = v0.z - height;
|
||||
let b = v1.z - height;
|
||||
let c = v2.z - height;
|
||||
|
||||
if dot0 == 0.0 || dot1 == 0.0 || dot2 == 0.0 {
|
||||
dot0 -= 0.0001;
|
||||
dot1 -= 0.0001;
|
||||
dot2 -= 0.0001;
|
||||
}
|
||||
let a_pos = a > 0.0;
|
||||
let b_pos = b > 0.0;
|
||||
let c_pos = c > 0.0;
|
||||
|
||||
let mut result = [Pos::zeros(); 2];
|
||||
let mut index = 0;
|
||||
|
||||
if dot0 * dot1 < 0.0 {
|
||||
let t = dot0 / (dot0 - dot1);
|
||||
if a_pos ^ b_pos {
|
||||
let t = a / (a - b);
|
||||
let intersection = v0 + t * (v1 - v0);
|
||||
result[index] = intersection;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if dot1 * dot2 < 0.0 {
|
||||
let t = dot1 / (dot1 - dot2);
|
||||
if b_pos ^ c_pos {
|
||||
let t = b / (b - c);
|
||||
let intersection = v1 + t * (v2 - v1);
|
||||
result[index] = intersection;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if dot2 * dot0 < 0.0 {
|
||||
let t = dot2 / (dot2 - dot0);
|
||||
if c_pos ^ a_pos {
|
||||
let t = c / (c - a);
|
||||
let intersection = v2 + t * (v0 - v2);
|
||||
result[index] = intersection;
|
||||
}
|
||||
|
@@ -1,166 +1,212 @@
|
||||
use nalgebra::{Vector2, Vector3};
|
||||
use ordered_float::OrderedFloat;
|
||||
use std::{
|
||||
ops::Deref,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Condvar, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::mesh::Mesh;
|
||||
use ordered_float::OrderedFloat;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
use crate::{config::SliceConfig, mesh::Mesh};
|
||||
use goo_format::{File as GooFile, HeaderInfo, LayerContent, LayerEncoder};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SliceConfig {
|
||||
pub platform_resolution: Vector2<u32>,
|
||||
pub platform_size: Vector3<f32>,
|
||||
pub slice_height: f32,
|
||||
|
||||
pub exposure_config: ExposureConfig,
|
||||
pub first_exposure_config: ExposureConfig,
|
||||
pub first_layers: u32,
|
||||
pub struct Slicer {
|
||||
slice_config: SliceConfig,
|
||||
model: Mesh,
|
||||
progress: Progress,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExposureConfig {
|
||||
pub exposure_time: f32,
|
||||
pub lift_distance: f32,
|
||||
pub lift_speed: f32,
|
||||
pub retract_distance: f32,
|
||||
pub retract_speed: f32,
|
||||
#[derive(Clone)]
|
||||
pub struct Progress {
|
||||
inner: Arc<ProgressInner>,
|
||||
}
|
||||
|
||||
pub fn slice_goo(slice_config: &SliceConfig, model: &Mesh, progress: impl Fn(u32, u32)) -> GooFile {
|
||||
let (_, max) = model.minmax_point();
|
||||
let max = model.transform(&max);
|
||||
let layers = (max.z / slice_config.slice_height).ceil() as u32;
|
||||
pub struct ProgressInner {
|
||||
completed: AtomicU32,
|
||||
total: u32,
|
||||
|
||||
let layers = (0..layers)
|
||||
.inspect(|&layer| {
|
||||
progress(layer, layers);
|
||||
})
|
||||
.map(|layer| {
|
||||
let height = layer as f32 * slice_config.slice_height;
|
||||
let intersections = model.intersect_plane(height);
|
||||
notify: Condvar,
|
||||
last_completed: Mutex<u32>,
|
||||
}
|
||||
|
||||
let segments = intersections
|
||||
.chunks(2)
|
||||
.map(|x| (x[0], x[1]))
|
||||
.collect::<Vec<_>>();
|
||||
impl Slicer {
|
||||
pub fn new(slice_config: SliceConfig, model: Mesh) -> Self {
|
||||
let max = model.transform(&model.minmax_point().1);
|
||||
let layers = (max.z / slice_config.slice_height).ceil() as u32;
|
||||
|
||||
let mut out = Vec::new();
|
||||
for y in 0..slice_config.platform_resolution.y {
|
||||
let mut intersections = segments
|
||||
.iter()
|
||||
.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
|
||||
}
|
||||
})
|
||||
Self {
|
||||
slice_config,
|
||||
model,
|
||||
progress: Progress {
|
||||
inner: Arc::new(ProgressInner {
|
||||
completed: AtomicU32::new(0),
|
||||
total: layers,
|
||||
|
||||
notify: Condvar::new(),
|
||||
last_completed: Mutex::new(0),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn progress(&self) -> Progress {
|
||||
self.progress.clone()
|
||||
}
|
||||
|
||||
pub fn slice(&self) -> GooFile {
|
||||
let (slice_config, model) = (&self.slice_config, &self.model);
|
||||
let layers = (0..self.progress.total)
|
||||
.into_par_iter()
|
||||
.inspect(|_| {
|
||||
self.progress.completed.fetch_add(1, Ordering::Relaxed);
|
||||
self.progress.notify.notify_all();
|
||||
})
|
||||
.map(|layer| {
|
||||
let height = layer as f32 * slice_config.slice_height;
|
||||
let intersections = model.intersect_plane(height);
|
||||
|
||||
let segments = intersections
|
||||
.chunks(2)
|
||||
.map(|x| (x[0], x[1]))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
intersections.sort_by_key(|&x| OrderedFloat(x));
|
||||
intersections.dedup();
|
||||
let mut out = Vec::new();
|
||||
for y in 0..slice_config.platform_resolution.y {
|
||||
let mut intersections = segments
|
||||
.iter()
|
||||
.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<_>>();
|
||||
|
||||
for span in intersections.chunks_exact(2) {
|
||||
let y_offset = (slice_config.platform_resolution.x * y) as u64;
|
||||
out.push((y_offset + span[0] as u64, y_offset + 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);
|
||||
intersections.sort_by_key(|&x| OrderedFloat(x));
|
||||
for span in intersections.chunks_exact(2) {
|
||||
let y_offset = (slice_config.platform_resolution.x * y) as u64;
|
||||
out.push((y_offset + span[0] as u64, y_offset + span[1] as u64));
|
||||
}
|
||||
}
|
||||
|
||||
if start > end {
|
||||
eprintln!("Invalid run {}-{}", start, end);
|
||||
} else {
|
||||
let mut encoder = LayerEncoder::new();
|
||||
|
||||
let mut last = 0;
|
||||
for (start, end) in out {
|
||||
if start > last {
|
||||
encoder.add_run(start - last, 0);
|
||||
}
|
||||
|
||||
assert!(end >= start);
|
||||
encoder.add_run(end - start, 255);
|
||||
last = end;
|
||||
}
|
||||
}
|
||||
|
||||
let image_size = slice_config.platform_resolution.x as u64
|
||||
* slice_config.platform_resolution.y as u64;
|
||||
encoder.add_run(image_size - last, 0);
|
||||
let image_size = slice_config.platform_resolution.x as u64
|
||||
* slice_config.platform_resolution.y as u64;
|
||||
encoder.add_run(image_size - last, 0);
|
||||
|
||||
let (data, checksum) = encoder.finish();
|
||||
let layer_exposure = if layer < slice_config.first_layers {
|
||||
&slice_config.first_exposure_config
|
||||
} else {
|
||||
&slice_config.exposure_config
|
||||
};
|
||||
let (data, checksum) = encoder.finish();
|
||||
let layer_exposure = if layer < slice_config.first_layers {
|
||||
&slice_config.first_exposure_config
|
||||
} else {
|
||||
&slice_config.exposure_config
|
||||
};
|
||||
|
||||
LayerContent {
|
||||
data,
|
||||
checksum,
|
||||
layer_position_z: slice_config.slice_height * (layer + 1) as f32,
|
||||
LayerContent {
|
||||
data,
|
||||
checksum,
|
||||
layer_position_z: slice_config.slice_height * (layer + 1) as f32,
|
||||
|
||||
layer_exposure_time: layer_exposure.exposure_time,
|
||||
lift_distance: layer_exposure.lift_distance,
|
||||
lift_speed: layer_exposure.lift_speed,
|
||||
retract_distance: layer_exposure.retract_distance,
|
||||
retract_speed: layer_exposure.retract_speed,
|
||||
pause_position_z: slice_config.platform_size.z,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let layer_time = slice_config.exposure_config.exposure_time
|
||||
+ slice_config.exposure_config.lift_distance / slice_config.exposure_config.lift_speed;
|
||||
let bottom_layer_time = slice_config.first_exposure_config.exposure_time
|
||||
+ slice_config.first_exposure_config.lift_distance
|
||||
/ slice_config.first_exposure_config.lift_speed;
|
||||
let total_time = (layers.len() as u32 - slice_config.first_layers) as f32 * layer_time
|
||||
+ slice_config.first_layers as f32 * bottom_layer_time;
|
||||
|
||||
GooFile::new(
|
||||
HeaderInfo {
|
||||
x_resolution: slice_config.platform_resolution.x as u16,
|
||||
y_resolution: slice_config.platform_resolution.y as u16,
|
||||
x_size: slice_config.platform_size.x,
|
||||
y_size: slice_config.platform_size.y,
|
||||
|
||||
layer_count: layers.len() as u32,
|
||||
printing_time: total_time as u32,
|
||||
layer_thickness: slice_config.slice_height,
|
||||
bottom_layers: slice_config.first_layers,
|
||||
transition_layers: slice_config.first_layers as u16 + 1,
|
||||
|
||||
exposure_time: slice_config.exposure_config.exposure_time,
|
||||
lift_distance: slice_config.exposure_config.lift_distance,
|
||||
lift_speed: slice_config.exposure_config.lift_speed,
|
||||
retract_distance: slice_config.exposure_config.retract_distance,
|
||||
retract_speed: slice_config.exposure_config.retract_speed,
|
||||
|
||||
bottom_exposure_time: slice_config.first_exposure_config.exposure_time,
|
||||
bottom_lift_distance: slice_config.first_exposure_config.lift_distance,
|
||||
bottom_lift_speed: slice_config.first_exposure_config.lift_speed,
|
||||
bottom_retract_distance: slice_config.first_exposure_config.retract_distance,
|
||||
bottom_retract_speed: slice_config.first_exposure_config.retract_speed,
|
||||
|
||||
layer_exposure_time: layer_exposure.exposure_time,
|
||||
lift_distance: layer_exposure.lift_distance,
|
||||
lift_speed: layer_exposure.lift_speed,
|
||||
retract_distance: layer_exposure.retract_distance,
|
||||
retract_speed: layer_exposure.retract_speed,
|
||||
pause_position_z: slice_config.platform_size.z,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let layer_time = slice_config.exposure_config.exposure_time
|
||||
+ slice_config.exposure_config.lift_distance / slice_config.exposure_config.lift_speed;
|
||||
let bottom_layer_time = slice_config.first_exposure_config.exposure_time
|
||||
+ slice_config.first_exposure_config.lift_distance
|
||||
/ slice_config.first_exposure_config.lift_speed;
|
||||
let total_time = (layers.len() as u32 - slice_config.first_layers) as f32 * layer_time
|
||||
+ slice_config.first_layers as f32 * bottom_layer_time;
|
||||
|
||||
GooFile::new(
|
||||
HeaderInfo {
|
||||
x_resolution: slice_config.platform_resolution.x as u16,
|
||||
y_resolution: slice_config.platform_resolution.y as u16,
|
||||
x_size: slice_config.platform_size.x,
|
||||
y_size: slice_config.platform_size.y,
|
||||
|
||||
layer_count: layers.len() as u32,
|
||||
printing_time: total_time as u32,
|
||||
layer_thickness: slice_config.slice_height,
|
||||
bottom_layers: slice_config.first_layers,
|
||||
transition_layers: slice_config.first_layers as u16 + 1,
|
||||
|
||||
exposure_time: slice_config.exposure_config.exposure_time,
|
||||
lift_distance: slice_config.exposure_config.lift_distance,
|
||||
lift_speed: slice_config.exposure_config.lift_speed,
|
||||
retract_distance: slice_config.exposure_config.retract_distance,
|
||||
retract_speed: slice_config.exposure_config.retract_speed,
|
||||
|
||||
bottom_exposure_time: slice_config.first_exposure_config.exposure_time,
|
||||
bottom_lift_distance: slice_config.first_exposure_config.lift_distance,
|
||||
bottom_lift_speed: slice_config.first_exposure_config.lift_speed,
|
||||
bottom_retract_distance: slice_config.first_exposure_config.retract_distance,
|
||||
bottom_retract_speed: slice_config.first_exposure_config.retract_speed,
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
layers,
|
||||
)
|
||||
}
|
||||
|
||||
impl Default for ExposureConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
exposure_time: 3.0,
|
||||
lift_distance: 5.0,
|
||||
lift_speed: 65.0,
|
||||
retract_distance: 5.0,
|
||||
retract_speed: 150.0,
|
||||
}
|
||||
},
|
||||
layers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Progress {
|
||||
type Target = ProgressInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Progress {
|
||||
pub fn wait(&self) -> u32 {
|
||||
let mut last_completed = self
|
||||
.notify
|
||||
.wait(self.last_completed.lock().unwrap())
|
||||
.unwrap();
|
||||
|
||||
let current = self.completed.load(Ordering::Relaxed);
|
||||
if *last_completed < current {
|
||||
*last_completed = current;
|
||||
}
|
||||
|
||||
current
|
||||
}
|
||||
|
||||
pub fn completed(&self) -> u32 {
|
||||
self.completed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn total(&self) -> u32 {
|
||||
self.total
|
||||
}
|
||||
}
|
||||
|
@@ -18,3 +18,4 @@ wgpu = "0.19.4"
|
||||
common = { path = "../common" }
|
||||
goo_format = { path = "../goo_format" }
|
||||
slicer = { path = "../slicer" }
|
||||
clone-macro = "0.1.0"
|
||||
|
@@ -1,11 +1,15 @@
|
||||
use std::{
|
||||
sync::{atomic::AtomicU32, Arc, Mutex, RwLock},
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use egui::{CentralPanel, Frame, Sense};
|
||||
use egui_wgpu::Callback;
|
||||
use nalgebra::{Vector2, Vector3};
|
||||
use slicer::{
|
||||
config::{ExposureConfig, SliceConfig},
|
||||
slicer::Progress as SliceProgress,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
@@ -15,27 +19,20 @@ use crate::{
|
||||
windows::{self, Windows},
|
||||
};
|
||||
use goo_format::File as GooFile;
|
||||
use slicer::slicer::{ExposureConfig, SliceConfig};
|
||||
|
||||
pub struct App {
|
||||
pub camera: Camera,
|
||||
pub slice_config: SliceConfig,
|
||||
pub meshes: Arc<RwLock<Vec<RenderedMesh>>>,
|
||||
|
||||
pub slice_progress: Option<Arc<SliceProgress>>,
|
||||
pub slice_progress: Option<SliceProgress>,
|
||||
pub slice_result: Arc<Mutex<Option<SliceResult>>>,
|
||||
|
||||
pub render_style: RenderStyle,
|
||||
pub fps: FpsTracker,
|
||||
pub windows: Windows,
|
||||
}
|
||||
|
||||
pub struct SliceProgress {
|
||||
pub current: AtomicU32,
|
||||
pub total: AtomicU32,
|
||||
|
||||
pub result: Mutex<Option<SliceResult>>,
|
||||
}
|
||||
|
||||
pub struct SliceResult {
|
||||
pub goo: GooFile,
|
||||
|
||||
@@ -120,6 +117,7 @@ impl Default for App {
|
||||
},
|
||||
fps: FpsTracker::new(),
|
||||
slice_progress: None,
|
||||
slice_result: Arc::new(Mutex::new(None)),
|
||||
meshes: Arc::new(RwLock::new(Vec::new())),
|
||||
windows: Windows::default(),
|
||||
render_style: RenderStyle::Normals,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use eframe::Frame;
|
||||
use egui::{Context, DragValue, Grid, Ui, Window};
|
||||
use slicer::slicer::ExposureConfig;
|
||||
use slicer::config::ExposureConfig;
|
||||
|
||||
use crate::{
|
||||
app::App,
|
||||
|
@@ -9,93 +9,86 @@ use crate::{
|
||||
};
|
||||
|
||||
pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
|
||||
if let Some(slice_progress) = &app.slice_progress {
|
||||
if let Some(result) = slice_progress.result.lock().unwrap().as_mut() {
|
||||
Window::new("Slice Preview")
|
||||
.resizable([true, true])
|
||||
.show(ctx, move |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(
|
||||
Slider::new(
|
||||
&mut result.slice_preview_layer,
|
||||
1..=result.goo.layers.len(),
|
||||
)
|
||||
if let Some(result) = app.slice_result.lock().unwrap().as_mut() {
|
||||
Window::new("Slice Preview")
|
||||
.resizable([true, true])
|
||||
.show(ctx, move |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(
|
||||
Slider::new(&mut result.slice_preview_layer, 1..=result.goo.layers.len())
|
||||
.vertical()
|
||||
.show_value(false),
|
||||
);
|
||||
|
||||
let new_preview = if result.last_preview_layer != result.slice_preview_layer {
|
||||
result.last_preview_layer = result.slice_preview_layer;
|
||||
let (width, height) = (
|
||||
result.goo.header.x_resolution as u32,
|
||||
result.goo.header.y_resolution as u32,
|
||||
);
|
||||
|
||||
let new_preview = if result.last_preview_layer != result.slice_preview_layer
|
||||
{
|
||||
result.last_preview_layer = result.slice_preview_layer;
|
||||
let (width, height) = (
|
||||
result.goo.header.x_resolution as u32,
|
||||
result.goo.header.y_resolution as u32,
|
||||
);
|
||||
let layer_data = &result.goo.layers[result.slice_preview_layer - 1].data;
|
||||
let decoder = LayerDecoder::new(layer_data);
|
||||
|
||||
let layer_data =
|
||||
&result.goo.layers[result.slice_preview_layer - 1].data;
|
||||
let decoder = LayerDecoder::new(layer_data);
|
||||
|
||||
let mut image = vec![0; (width * height) as usize];
|
||||
let mut pixel = 0;
|
||||
for run in decoder {
|
||||
for _ in 0..run.length {
|
||||
image[pixel] = run.value;
|
||||
pixel += 1;
|
||||
}
|
||||
let mut image = vec![0; (width * height) as usize];
|
||||
let mut pixel = 0;
|
||||
for run in decoder {
|
||||
for _ in 0..run.length {
|
||||
image[pixel] = run.value;
|
||||
pixel += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Some(image)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(image)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||
let available_size = ui.available_size();
|
||||
let (rect, _response) = ui.allocate_exact_size(
|
||||
Vec2::new(
|
||||
available_size.x,
|
||||
available_size.x / result.goo.header.x_resolution as f32
|
||||
* result.goo.header.y_resolution as f32,
|
||||
),
|
||||
Sense::drag(),
|
||||
);
|
||||
let callback = Callback::new_paint_callback(
|
||||
rect,
|
||||
SlicePreviewRenderCallback {
|
||||
dimensions: Vector2::new(
|
||||
result.goo.header.x_resolution as u32,
|
||||
result.goo.header.y_resolution as u32,
|
||||
),
|
||||
offset: result.preview_offset,
|
||||
scale: result.preview_scale,
|
||||
new_preview,
|
||||
},
|
||||
);
|
||||
ui.painter().add(callback);
|
||||
});
|
||||
});
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(
|
||||
DragValue::new(&mut result.slice_preview_layer)
|
||||
.clamp_range(1..=result.goo.layers.len())
|
||||
.suffix(format!("/{}", result.goo.layers.len())),
|
||||
egui::Frame::canvas(ui.style()).show(ui, |ui| {
|
||||
let available_size = ui.available_size();
|
||||
let (rect, _response) = ui.allocate_exact_size(
|
||||
Vec2::new(
|
||||
available_size.x,
|
||||
available_size.x / result.goo.header.x_resolution as f32
|
||||
* result.goo.header.y_resolution as f32,
|
||||
),
|
||||
Sense::drag(),
|
||||
);
|
||||
result.slice_preview_layer +=
|
||||
ui.button(RichText::new("+").monospace()).clicked() as usize;
|
||||
result.slice_preview_layer -=
|
||||
ui.button(RichText::new("-").monospace()).clicked() as usize;
|
||||
|
||||
ui.separator();
|
||||
ui.label("Offset");
|
||||
vec2_dragger(ui, result.preview_offset.as_mut(), |x| x);
|
||||
|
||||
ui.separator();
|
||||
ui.label("Scale");
|
||||
ui.add(DragValue::new(&mut result.preview_scale));
|
||||
let callback = Callback::new_paint_callback(
|
||||
rect,
|
||||
SlicePreviewRenderCallback {
|
||||
dimensions: Vector2::new(
|
||||
result.goo.header.x_resolution as u32,
|
||||
result.goo.header.y_resolution as u32,
|
||||
),
|
||||
offset: result.preview_offset,
|
||||
scale: result.preview_scale,
|
||||
new_preview,
|
||||
},
|
||||
);
|
||||
ui.painter().add(callback);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(
|
||||
DragValue::new(&mut result.slice_preview_layer)
|
||||
.clamp_range(1..=result.goo.layers.len())
|
||||
.suffix(format!("/{}", result.goo.layers.len())),
|
||||
);
|
||||
result.slice_preview_layer +=
|
||||
ui.button(RichText::new("+").monospace()).clicked() as usize;
|
||||
result.slice_preview_layer -=
|
||||
ui.button(RichText::new("-").monospace()).clicked() as usize;
|
||||
|
||||
ui.separator();
|
||||
ui.label("Offset");
|
||||
vec2_dragger(ui, result.preview_offset.as_mut(), |x| x);
|
||||
|
||||
ui.separator();
|
||||
ui.label("Scale");
|
||||
ui.add(DragValue::new(&mut result.preview_scale));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
use std::{fs::File, io::Write, sync::atomic::Ordering};
|
||||
use std::{fs::File, io::Write};
|
||||
|
||||
use common::serde::DynamicSerializer;
|
||||
use eframe::Frame;
|
||||
@@ -12,8 +12,7 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
|
||||
let mut save_complete = false;
|
||||
|
||||
if let Some(progress) = app.slice_progress.as_ref() {
|
||||
let current = progress.current.load(Ordering::Relaxed) + 1;
|
||||
let total = progress.total.load(Ordering::Relaxed);
|
||||
let (current, total) = (progress.completed(), progress.total());
|
||||
|
||||
let mut window = Window::new("Slice Progress");
|
||||
|
||||
@@ -33,7 +32,7 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
|
||||
} else {
|
||||
ui.label("Slicing complete!");
|
||||
if ui.button("Save").clicked() {
|
||||
let result = progress.result.lock().unwrap().take().unwrap();
|
||||
let result = app.slice_result.lock().unwrap().take().unwrap();
|
||||
if let Some(path) = FileDialog::new().save_file() {
|
||||
let mut file = File::create(path).unwrap();
|
||||
let mut serializer = DynamicSerializer::new();
|
||||
|
@@ -1,19 +1,14 @@
|
||||
use std::{
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
thread,
|
||||
};
|
||||
use std::thread;
|
||||
|
||||
use clone_macro::clone;
|
||||
use eframe::Frame;
|
||||
use egui::{Context, TopBottomPanel, Ui};
|
||||
use nalgebra::Vector2;
|
||||
use rfd::FileDialog;
|
||||
use slicer::{slicer::slice_goo, Pos};
|
||||
use slicer::{slicer::Slicer, Pos};
|
||||
|
||||
use crate::{
|
||||
app::{App, SliceProgress, SliceResult},
|
||||
app::{App, SliceResult},
|
||||
render::rendered_mesh::RenderedMesh,
|
||||
};
|
||||
|
||||
@@ -83,26 +78,26 @@ pub fn ui(app: &mut App, ctx: &Context, _frame: &mut Frame) {
|
||||
mesh.position.z - app.slice_config.slice_height,
|
||||
);
|
||||
|
||||
let progress = Arc::new(SliceProgress {
|
||||
current: AtomicU32::new(0),
|
||||
total: AtomicU32::new(0),
|
||||
result: Mutex::new(None),
|
||||
});
|
||||
app.slice_progress = Some(progress.clone());
|
||||
// let progress = Arc::new(SliceProgress {
|
||||
// current: AtomicU32::new(0),
|
||||
// total: AtomicU32::new(0),
|
||||
// result: Mutex::new(None),
|
||||
// });
|
||||
// app.slice_progress = Some(progress.clone());
|
||||
|
||||
thread::spawn(move || {
|
||||
let goo = slice_goo(&slice_config, &mesh, |current, total| {
|
||||
progress.current.store(current, Ordering::Relaxed);
|
||||
progress.total.store(total, Ordering::Relaxed);
|
||||
});
|
||||
progress.result.lock().unwrap().replace(SliceResult {
|
||||
let slicer = Slicer::new(slice_config, mesh);
|
||||
app.slice_progress = Some(slicer.progress());
|
||||
|
||||
thread::spawn(clone!([{ app.slice_result } as slice_result], move || {
|
||||
let goo = slicer.slice();
|
||||
slice_result.lock().unwrap().replace(SliceResult {
|
||||
goo,
|
||||
slice_preview_layer: 0,
|
||||
last_preview_layer: 0,
|
||||
preview_offset: Vector2::new(0.0, 0.0),
|
||||
preview_scale,
|
||||
});
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user