Abstraction over format
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4418,9 +4418,11 @@ dependencies = [
|
||||
"common",
|
||||
"criterion",
|
||||
"goo_format",
|
||||
"image 0.25.1",
|
||||
"nalgebra 0.32.6",
|
||||
"obj-rs",
|
||||
"ordered-float",
|
||||
"parking_lot",
|
||||
"rayon",
|
||||
"stl_io",
|
||||
"tracing",
|
||||
|
4
TODO.md
4
TODO.md
@@ -75,6 +75,8 @@
|
||||
- [x] Fix Z translation being doubled
|
||||
- [ ] Merge goo_format changes into goo crate
|
||||
- [ ] Implement .ctb format (see <https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc>)
|
||||
- [x] Generic format system
|
||||
- [ ] Fix plugins / post processes
|
||||
- [ ] Undo / Redo
|
||||
- [x] Close file menu if button clicked
|
||||
- [x] Allow dragging in project to load them
|
||||
@@ -82,5 +84,5 @@
|
||||
- [ ] Save on remote thread
|
||||
- [ ] Printer profiles
|
||||
- [x] Save config on quit
|
||||
- [ ] Refactor plugins to post processors
|
||||
- [x] Refactor plugins to post processors
|
||||
- [ ] Update readme
|
||||
|
@@ -1,8 +1,12 @@
|
||||
use nalgebra::{Vector2, Vector3};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::format::Format;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SliceConfig {
|
||||
pub format: Format,
|
||||
|
||||
pub platform_resolution: Vector2<u32>,
|
||||
pub platform_size: Vector3<f32>,
|
||||
pub slice_height: f32,
|
||||
@@ -25,6 +29,8 @@ pub struct ExposureConfig {
|
||||
impl Default for SliceConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
format: Format::Goo,
|
||||
|
||||
platform_resolution: Vector2::new(11_520, 5_120),
|
||||
platform_size: Vector3::new(218.88, 122.904, 260.0),
|
||||
slice_height: 0.05,
|
||||
|
6
common/src/format.rs
Normal file
6
common/src/format.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub enum Format {
|
||||
Goo,
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
pub mod config;
|
||||
pub mod format;
|
||||
pub mod image;
|
||||
pub mod misc;
|
||||
pub mod oklab;
|
||||
|
@@ -13,7 +13,6 @@ use egui::Visuals;
|
||||
use egui_dock::{DockState, NodeIndex};
|
||||
use egui_phosphor::regular::CARET_RIGHT;
|
||||
use egui_tracing::EventCollector;
|
||||
use image::imageops::FilterType;
|
||||
use nalgebra::Vector2;
|
||||
use parking_lot::RwLock;
|
||||
use tracing::{info, warn};
|
||||
@@ -33,8 +32,7 @@ use crate::{
|
||||
windows::{self, Tab},
|
||||
};
|
||||
use common::config::SliceConfig;
|
||||
use goo_format::{File as GooFile, LayerEncoder, PreviewImage};
|
||||
use slicer::{slicer::Slicer, Pos};
|
||||
use slicer::{format::FormatSliceFile, slicer::Slicer, Pos};
|
||||
|
||||
pub mod config;
|
||||
pub mod project;
|
||||
@@ -174,19 +172,12 @@ impl App {
|
||||
[{ self.slice_operation } as slice_operation],
|
||||
move || {
|
||||
let slice_operation = slice_operation.as_ref().unwrap();
|
||||
let mut goo = GooFile::from_slice_result(slicer.slice::<LayerEncoder>());
|
||||
let preview_image = slice_operation.preview_image();
|
||||
let file = FormatSliceFile::from_slice_result(preview_image, slicer.slice_format());
|
||||
|
||||
{
|
||||
let preview_image = slice_operation.preview_image();
|
||||
goo.header.big_preview =
|
||||
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
|
||||
goo.header.small_preview =
|
||||
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
|
||||
}
|
||||
|
||||
let layers = goo.layers.len();
|
||||
let layers = file.info().layers as usize;
|
||||
slice_operation.add_result(SliceResult {
|
||||
goo,
|
||||
file,
|
||||
slice_preview_layer: 0,
|
||||
last_preview_layer: 0,
|
||||
preview_offset: Vector2::new(0.0, 0.0),
|
||||
|
@@ -7,11 +7,10 @@ use std::{
|
||||
};
|
||||
|
||||
use common::misc::human_duration;
|
||||
use goo_format::File as GooFile;
|
||||
use image::RgbaImage;
|
||||
use nalgebra::Vector2;
|
||||
use parking_lot::{Condvar, MappedMutexGuard, Mutex, MutexGuard};
|
||||
use slicer::slicer::Progress as SliceProgress;
|
||||
use slicer::{format::FormatSliceFile, slicer::Progress as SliceProgress};
|
||||
use tracing::info;
|
||||
|
||||
use crate::app::App;
|
||||
@@ -30,7 +29,7 @@ pub struct SliceOperation {
|
||||
}
|
||||
|
||||
pub struct SliceResult {
|
||||
pub goo: GooFile,
|
||||
pub file: FormatSliceFile,
|
||||
|
||||
pub slice_preview_layer: usize,
|
||||
pub last_preview_layer: usize,
|
||||
@@ -95,7 +94,7 @@ impl SliceOperation {
|
||||
return;
|
||||
}
|
||||
|
||||
let goo = &mut result.as_mut().unwrap().goo;
|
||||
let goo = &mut result.as_mut().unwrap().file;
|
||||
app.plugin_manager.post_slice(app, goo);
|
||||
self.has_post_processed = true;
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
use egui::{Context, Ui};
|
||||
use slicer::format::FormatSliceFile;
|
||||
|
||||
use crate::app::App;
|
||||
use goo_format::File as GooFile;
|
||||
@@ -18,9 +19,10 @@ pub struct PluginManager {
|
||||
}
|
||||
|
||||
impl PluginManager {
|
||||
pub fn post_slice(&self, app: &App, goo: &mut GooFile) {
|
||||
pub fn post_slice(&self, app: &App, goo: &mut FormatSliceFile) {
|
||||
for plugin in &self.plugins {
|
||||
plugin.post_slice(app, goo);
|
||||
// TODO: FIX PLUGINS
|
||||
// plugin.post_slice(app, goo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,6 @@ use egui::{
|
||||
};
|
||||
use egui_phosphor::regular::{FLOPPY_DISK_BACK, PAPER_PLANE_TILT};
|
||||
use egui_wgpu::Callback;
|
||||
use goo_format::LayerDecoder;
|
||||
use nalgebra::Vector2;
|
||||
use rfd::FileDialog;
|
||||
|
||||
@@ -82,7 +81,7 @@ pub fn ui(app: &mut App, ctx: &Context) {
|
||||
let result = result.as_ref().unwrap();
|
||||
|
||||
let mut serializer = DynamicSerializer::new();
|
||||
result.goo.serialize(&mut serializer);
|
||||
result.file.serialize(&mut serializer);
|
||||
let data = Arc::new(serializer.into_inner());
|
||||
|
||||
let mainboard_id = printer.mainboard_id.clone();
|
||||
@@ -101,7 +100,7 @@ pub fn ui(app: &mut App, ctx: &Context) {
|
||||
if let Some(path) = FileDialog::new().save_file() {
|
||||
let mut file = File::create(path).unwrap();
|
||||
let mut serializer = DynamicSerializer::new();
|
||||
result.goo.serialize(&mut serializer);
|
||||
result.file.serialize(&mut serializer);
|
||||
file.write_all(&serializer.into_inner()).unwrap();
|
||||
}
|
||||
}
|
||||
@@ -120,7 +119,7 @@ pub fn ui(app: &mut App, ctx: &Context) {
|
||||
let layer_digits = result.layer_count.1 as usize;
|
||||
ui.add(
|
||||
DragValue::new(&mut result.slice_preview_layer)
|
||||
.clamp_range(1..=result.goo.layers.len())
|
||||
.clamp_range(1..=result.file.info().layers)
|
||||
.custom_formatter(|n, _| {
|
||||
format!("{:0>layer_digits$}/{}", n, result.layer_count.0)
|
||||
}),
|
||||
@@ -152,38 +151,27 @@ pub fn ui(app: &mut App, ctx: &Context) {
|
||||
}
|
||||
|
||||
fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
|
||||
let info = result.file.info();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().slider_width = ui.available_size().x
|
||||
/ result.goo.header.x_resolution as f32
|
||||
* result.goo.header.y_resolution as f32
|
||||
- 10.0;
|
||||
ui.spacing_mut().slider_width =
|
||||
ui.available_size().x / info.resolution.x as f32 * info.resolution.y as f32 - 10.0;
|
||||
ui.add(
|
||||
Slider::new(&mut result.slice_preview_layer, 1..=result.goo.layers.len())
|
||||
Slider::new(&mut result.slice_preview_layer, 1..=info.layers as usize)
|
||||
.vertical()
|
||||
.handle_shape(HandleShape::Rect { aspect_ratio: 1.0 })
|
||||
.show_value(false),
|
||||
);
|
||||
|
||||
let (width, height) = (
|
||||
result.goo.header.x_resolution as u32,
|
||||
result.goo.header.y_resolution as u32,
|
||||
);
|
||||
let (width, height) = (info.resolution.x as u32, info.resolution.y as u32);
|
||||
|
||||
result.slice_preview_layer = result.slice_preview_layer.clamp(1, result.goo.layers.len());
|
||||
result.slice_preview_layer = result.slice_preview_layer.clamp(1, info.layers as usize);
|
||||
let new_preview = if result.last_preview_layer != result.slice_preview_layer {
|
||||
result.last_preview_layer = result.slice_preview_layer;
|
||||
|
||||
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 layer = result.slice_preview_layer - 1;
|
||||
result.file.decode_layer(layer, &mut image);
|
||||
|
||||
Some(image)
|
||||
} else {
|
||||
@@ -195,8 +183,7 @@ fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
|
||||
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,
|
||||
available_size.x / info.resolution.x as f32 * info.resolution.y as f32,
|
||||
),
|
||||
Sense::drag(),
|
||||
);
|
||||
@@ -215,10 +202,7 @@ fn slice_preview(ui: &mut egui::Ui, result: &mut SliceResult) {
|
||||
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,
|
||||
),
|
||||
dimensions: Vector2::new(info.resolution.x as u32, info.resolution.y as u32),
|
||||
offset: result.preview_offset,
|
||||
scale: preview_scale,
|
||||
new_preview,
|
||||
|
@@ -5,9 +5,11 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
image.workspace = true
|
||||
nalgebra.workspace = true
|
||||
obj-rs.workspace = true
|
||||
ordered-float.workspace = true
|
||||
parking_lot.workspace = true
|
||||
rayon.workspace = true
|
||||
stl_io.workspace = true
|
||||
tracing.workspace = true
|
||||
|
74
slicer/src/format.rs
Normal file
74
slicer/src/format.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use goo_format::PreviewImage;
|
||||
use image::{imageops::FilterType, RgbaImage};
|
||||
use nalgebra::Vector2;
|
||||
use parking_lot::MappedMutexGuard;
|
||||
|
||||
use common::{misc::SliceResult, serde::Serializer};
|
||||
|
||||
pub enum FormatSliceResult<'a> {
|
||||
Goo(SliceResult<'a, goo_format::LayerContent>),
|
||||
}
|
||||
|
||||
pub enum FormatSliceFile {
|
||||
Goo(goo_format::File),
|
||||
}
|
||||
|
||||
pub struct SliceInfo {
|
||||
pub layers: u32,
|
||||
pub resolution: Vector2<u32>,
|
||||
}
|
||||
|
||||
// TODO: convert to dynamic dispatch
|
||||
|
||||
impl FormatSliceFile {
|
||||
pub fn from_slice_result(
|
||||
preview_image: MappedMutexGuard<'_, RgbaImage>,
|
||||
slice_result: FormatSliceResult,
|
||||
) -> Self {
|
||||
match slice_result {
|
||||
FormatSliceResult::Goo(result) => {
|
||||
let mut file = goo_format::File::from_slice_result(result);
|
||||
file.header.big_preview =
|
||||
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
|
||||
file.header.small_preview =
|
||||
PreviewImage::from_image_scaled(&preview_image, FilterType::Nearest);
|
||||
Self::Goo(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serialize<T: Serializer>(&self, ser: &mut T) {
|
||||
match self {
|
||||
FormatSliceFile::Goo(file) => file.serialize(ser),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info(&self) -> SliceInfo {
|
||||
match self {
|
||||
FormatSliceFile::Goo(file) => SliceInfo {
|
||||
layers: file.header.layer_count,
|
||||
resolution: Vector2::new(
|
||||
file.header.x_resolution as u32,
|
||||
file.header.y_resolution as u32,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_layer(&self, layer: usize, image: &mut [u8]) {
|
||||
match self {
|
||||
FormatSliceFile::Goo(file) => {
|
||||
let layer_data = &file.layers[layer].data;
|
||||
let decoder = goo_format::LayerDecoder::new(layer_data);
|
||||
|
||||
let mut pixel = 0;
|
||||
for run in decoder {
|
||||
for _ in 0..run.length {
|
||||
image[pixel] = run.value;
|
||||
pixel += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,6 +3,7 @@
|
||||
use nalgebra::Vector3;
|
||||
|
||||
pub mod builder;
|
||||
pub mod format;
|
||||
pub mod half_edge;
|
||||
pub mod mesh;
|
||||
pub mod segments;
|
||||
|
@@ -10,6 +10,7 @@ use nalgebra::{Vector2, Vector3};
|
||||
|
||||
use common::{
|
||||
config::{ExposureConfig, SliceConfig},
|
||||
format::Format,
|
||||
serde::DynamicSerializer,
|
||||
};
|
||||
use goo_format::{File as GooFile, LayerEncoder};
|
||||
@@ -20,6 +21,8 @@ fn main() -> Result<()> {
|
||||
const OUTPUT_PATH: &str = "output.goo";
|
||||
|
||||
let slice_config = SliceConfig {
|
||||
format: Format::Goo,
|
||||
|
||||
platform_resolution: Vector2::new(11_520, 5_120),
|
||||
platform_size: Vector3::new(218.88, 122.904, 260.0),
|
||||
slice_height: 0.05,
|
||||
|
@@ -8,12 +8,13 @@ use std::{
|
||||
|
||||
use common::{
|
||||
config::SliceConfig,
|
||||
format::Format,
|
||||
misc::{EncodableLayer, SliceResult},
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
use crate::{mesh::Mesh, segments::Segments1D, Pos};
|
||||
use crate::{format::FormatSliceResult, mesh::Mesh, segments::Segments1D, Pos};
|
||||
|
||||
/// Used to slice a mesh.
|
||||
pub struct Slicer {
|
||||
@@ -68,6 +69,12 @@ impl Slicer {
|
||||
self.progress.clone()
|
||||
}
|
||||
|
||||
pub fn slice_format(&self) -> FormatSliceResult {
|
||||
match self.slice_config.format {
|
||||
Format::Goo => FormatSliceResult::Goo(self.slice::<goo_format::LayerEncoder>()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Actually runs the slicing operation, it is multithreaded.
|
||||
pub fn slice<Layer: EncodableLayer>(&self) -> SliceResult<Layer::Output> {
|
||||
let pixels = (self.slice_config.platform_resolution.x
|
||||
|
Reference in New Issue
Block a user