use std::{
io::{Cursor, Read, Seek, Write},
time::{SystemTime, UNIX_EPOCH},
};
use anyhow::Result;
use crate::{
container::{Image, Run},
progress::Progress,
serde::{DynamicSerializer, Serializer},
slice::{Format, SliceInfo, SliceResult, SlicedFile},
};
use image::{DynamicImage, RgbaImage};
use nalgebra::{Vector2, Vector3};
use serde::Serialize;
use zip::{ZipArchive, ZipWriter, write::FileOptions};
use crate::nanodlp::{
Layer, LayerDecoder, LayerEncoder, decode_png, encode_png,
layer::layers_bounds,
read_to_bytes,
types::{
Color, LayerInfo, Meta, Options, Plate, Profile, SHIELD_AFTER_LAYER, SHIELD_BEFORE_LAYER,
},
};
pub struct File {
pub meta: Meta,
pub plate: Plate,
pub options: Options,
pub profile: Profile,
pub preview: DynamicImage,
pub layer_info: Vec<LayerInfo>,
pub layers: Vec<Vec<u8>>, }
impl File {
pub fn from_slice_result(result: SliceResult<Layer>) -> Self {
let (layers, layer_info): (Vec<_>, Vec<_>) =
result.layers.into_iter().map(|x| (x.inner, x.info)).unzip();
let config = result.slice_config;
let pixel_size = Vector2::new(
config.platform_size.x / config.platform_resolution.x as f32,
config.platform_size.y / config.platform_resolution.y as f32,
);
let voxel_volume = pixel_size.x * pixel_size.y * config.slice_height;
let timestamp = (SystemTime::now().duration_since(UNIX_EPOCH).unwrap()).as_secs();
let (min, max) = layers_bounds(config, &layer_info);
Self {
meta: Default::default(),
plate: Plate {
processed: true,
total_solid_area: voxel_volume.convert() * result.voxels as f32,
layers_count: layers.len() as u32,
x_min: min.x,
x_max: max.x,
y_min: min.y,
y_max: max.y,
z_min: min.z,
z_max: max.z,
..Default::default()
},
options: Options {
p_width: config.platform_resolution.x,
p_height: config.platform_resolution.y,
thickness: config.slice_height.convert(),
x_offset: config.platform_resolution.x / 2,
y_offset: config.platform_resolution.y / 2,
x_pixel_size: pixel_size.x,
y_pixel_size: pixel_size.y,
x_res: pixel_size.x.convert(),
y_res: pixel_size.y.convert(),
ignore_mask: 1,
image_mirror: 1,
display_controller: 1,
support_layer_number: config.first_layers,
fill_color: "#ffffff".into(),
blank_color: "#000000".into(),
fill_color_rgb: Color::repeat(255),
blank_color_rgb: Color::repeat(0),
..Default::default()
},
profile: Profile {
title: "msla_format Config".into(),
depth: config.slice_height.convert(),
support_depth: config.slice_height.convert(),
transitional_layer: config.transition_layers,
updated: timestamp as u32,
cure_time: config.exposure_config.exposure_time,
support_cure_time: config.first_exposure_config.exposure_time,
fill_color: "#ffffff".into(),
blank_color: "#000000".into(),
ignore_mask: 1,
shield_before_layer: SHIELD_BEFORE_LAYER.into(),
shield_after_layer: SHIELD_AFTER_LAYER.into(),
..Default::default()
},
preview: Default::default(),
layer_info,
layers,
}
}
pub fn serialize<T: Serializer>(&self, ser: &mut T, progress: Progress) -> Result<()> {
let mut bytes = Vec::new();
let mut zip = ZipWriter::new(Cursor::new(&mut bytes));
fn serialize_file<W, T>(zip: &mut ZipWriter<W>, name: &str, value: &T) -> Result<()>
where
W: Write + Seek,
T: Serialize,
{
zip.start_file(name, FileOptions::DEFAULT)?;
serde_json::to_writer_pretty(zip, &value)?;
Ok(())
}
serialize_file(&mut zip, "meta.json", &self.meta)?;
serialize_file(&mut zip, "info.json", &self.layer_info)?;
serialize_file(&mut zip, "plate.json", &self.plate)?;
serialize_file(&mut zip, "profile.json", &self.profile)?;
serialize_file(&mut zip, "options.json", &self.options)?;
serialize_file(&mut zip, "slicer.json", &self.options)?;
zip.start_file("3d.png", FileOptions::DEFAULT)?;
zip.write_all(&encode_png(&self.preview)?)?;
progress.set_total(self.layers.len() as u64);
for (i, layer) in self.layers.iter().enumerate() {
progress.set_complete(i as u64);
zip.start_file(format!("{}.png", i + 1), FileOptions::DEFAULT)?;
zip.write_all(layer)?;
}
drop(zip);
ser.write_bytes(&bytes);
Ok(())
}
pub fn deserialize<T: Read + Seek>(reader: T) -> Result<Self> {
let mut zip = ZipArchive::new(reader)?;
let meta = (zip.by_name("meta.json").ok())
.map(serde_json::from_reader::<_, Meta>)
.transpose()?
.unwrap_or_default();
let layer_info = serde_json::from_reader::<_, Vec<LayerInfo>>(zip.by_name("info.json")?)?;
let plate = serde_json::from_reader::<_, Plate>(zip.by_name("plate.json")?)?;
let profile = serde_json::from_reader::<_, Profile>(zip.by_name("profile.json")?)?;
let mut options = zip.by_name("options.json");
if options.is_err() {
drop(options);
options = zip.by_name("slicer.json");
}
let options = serde_json::from_reader::<_, Options>(options?)?;
let preview = decode_png(&read_to_bytes(zip.by_name("3d.png")?)?)?;
let layers = (0..layer_info.len())
.map(|i| read_to_bytes(zip.by_name(&format!("{}.png", i + 1))?))
.collect::<Result<Vec<_>>>()?;
Ok(File {
meta,
plate,
options,
profile,
preview,
layer_info,
layers,
})
}
}
impl SlicedFile for File {
fn serialize(&self, ser: &mut DynamicSerializer, progress: Progress) {
self.serialize(ser, progress).unwrap();
}
fn set_preview(&mut self, preview: &RgbaImage) {
self.preview = preview.to_owned().into();
}
fn info(&self) -> SliceInfo {
SliceInfo {
layers: self.layer_info.len() as u32,
resolution: Vector2::new(self.options.p_width, self.options.p_height),
size: Vector3::default(), bottom_layers: self.profile.support_layer_number,
}
}
fn format(&self) -> Format {
Format::NanoDLP
}
fn runs(&self, layer: usize) -> Box<dyn Iterator<Item = Run> + '_> {
let decoder = LayerDecoder::new(&self.layers[layer]);
Box::new(decoder.runs().collect::<Vec<_>>().into_iter())
}
fn overwrite_layer(&mut self, layer: usize, image: Image) {
let encoder = LayerEncoder::from_image(image);
self.layers[layer] = encoder.image_data();
}
}