Abstraction over format

This commit is contained in:
Connor Slade
2024-11-02 13:52:05 -04:00
parent 66977e44b6
commit a0f5ee5303
14 changed files with 132 additions and 52 deletions

2
Cargo.lock generated
View File

@@ -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",

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum Format {
Goo,
}

View File

@@ -1,4 +1,5 @@
pub mod config;
pub mod format;
pub mod image;
pub mod misc;
pub mod oklab;

View File

@@ -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),

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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,

View File

@@ -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
View 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;
}
}
}
}
}
}

View File

@@ -3,6 +3,7 @@
use nalgebra::Vector3;
pub mod builder;
pub mod format;
pub mod half_edge;
pub mod mesh;
pub mod segments;

View File

@@ -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,

View File

@@ -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