123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- use std::{
- fs::File,
- io::{Cursor, Read, Write},
- path::PathBuf,
- };
- use binrw::{BinRead, BinWriterExt};
- use ezcad::{
- array_of::ArrayOf,
- layer::Layer,
- objects::{
- circle::Circle,
- hatch::{Hatch, HatchFlag, HatchPattern, HatchSetting, Hatches},
- rectangle::Rectangle,
- Object, ObjectCore, Translate,
- },
- pen::Pen,
- types::{ObjectType, Point},
- };
- use log::{debug, warn};
- use rand::{seq::SliceRandom, thread_rng};
- use serde::{de, Deserialize, Serialize};
- #[derive(Debug, Serialize, Deserialize)]
- #[serde(rename_all = "PascalCase")]
- pub struct DeleteObjects {
- layer: usize,
- object: Option<usize>,
- }
- impl DeleteObjects {
- pub fn delete(&self, layers: &mut ArrayOf<Layer>) {
- debug!(
- "Deleting layer #{} {}",
- match &self.object {
- Some(object) => format!("object #{}", object),
- None => format!("all objects"),
- },
- self.layer,
- );
- let layer: &mut Layer = layers.get_mut(self.layer).expect("Invalid layer index");
- match self.object {
- Some(index) => {
- assert!(index < layer.objects.len(), "Invalid object index");
- layer.objects.remove(index);
- }
- None => layer.objects.clear(),
- }
- }
- }
- #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
- #[serde(rename_all = "PascalCase")]
- pub struct HatchConfig {
- line_spacing: f64,
- count: Option<u32>,
- edge_offset: Option<f64>,
- start_offset: Option<f64>,
- end_offset: Option<f64>,
- angle: Option<f64>,
- rotate_angle: Option<f64>,
- line_reduction: Option<f64>,
- loop_distance: Option<f64>,
- loop_count: Option<u32>,
- pattern: Option<HatchPattern>,
- follow_edge_once: Option<bool>,
- cross_hatch: Option<bool>,
- }
- impl From<HatchConfig> for HatchSetting {
- fn from(value: HatchConfig) -> Self {
- let mut flags: HatchFlag = match value.pattern {
- Some(pattern) => pattern.into(),
- None => HatchPattern::Directional.into(),
- };
- value
- .follow_edge_once
- .map(|x| flags.set_follow_edge_once(x.into()));
- value.cross_hatch.map(|x| flags.set_cross_hatch(x.into()));
- if value.start_offset.is_none() && value.end_offset.is_none() {
- flags.set_average_distribute_line(1);
- }
- let mut ret = Self::default();
- *ret.line_spacing = value.line_spacing;
- value.count.map(|x| *ret.count = x.into());
- value.edge_offset.map(|x| *ret.edge_offset = x.into());
- value.start_offset.map(|x| *ret.start_offset = x.into());
- value.end_offset.map(|x| *ret.end_offset = x.into());
- value.angle.map(|x| *ret.angle = x.into());
- value.rotate_angle.map(|x| *ret.rotate_angle = x.into());
- value.line_reduction.map(|x| *ret.line_reduction = x.into());
- value.loop_distance.map(|x| *ret.loop_distance = x.into());
- value.loop_count.map(|x| *ret.loop_count = x.into());
- *ret.flags = flags;
- *ret.enabled = true.into();
- ret
- }
- }
- #[derive(Debug, Serialize, Deserialize)]
- #[serde(rename_all = "PascalCase")]
- pub struct ArrayConfig {
- columns: usize,
- rows: usize,
- spacing: f64,
- randomize_order: bool,
- starting_pen: u32,
- }
- #[derive(Debug, Serialize, Deserialize, strum::Display)]
- #[serde(rename_all = "PascalCase")]
- pub enum InputObject {
- Rectangle {
- width: f64,
- height: f64,
- round_corner: Option<f64>,
- },
- Circle {
- radius: f64,
- },
- Import {
- path: PathBuf,
- },
- Existing {
- layer: usize,
- object: usize,
- },
- }
- impl InputObject {
- fn new(&self, layers: &ArrayOf<Layer>) -> Object {
- match self {
- InputObject::Rectangle {
- width,
- height,
- round_corner,
- } => Object::Rectangle(Rectangle {
- corner_a: Point::from((-width / 2.0, -height / 2.0)).into(),
- corner_b: Point::from((width / 2.0, height / 2.0)).into(),
- round_bottom_left: round_corner.unwrap_or(0.0).into(),
- round_bottom_right: round_corner.unwrap_or(0.0).into(),
- round_top_left: round_corner.unwrap_or(0.0).into(),
- round_top_right: round_corner.unwrap_or(0.0).into(),
- ..Default::default()
- }),
- InputObject::Circle { radius } => Object::Circle(Circle {
- radius: (*radius).into(),
- ..Default::default()
- }),
- InputObject::Import { path } => Object::read_from_file(path),
- InputObject::Existing { layer, object } => {
- let layer: &Layer = layers.get(*layer).expect("Invalid layer index");
- layer
- .objects
- .get(*object)
- .expect("Invalid object index")
- .clone()
- }
- }
- }
- }
- #[derive(Debug, Serialize, Deserialize)]
- #[serde(rename_all = "PascalCase")]
- pub struct ObjectOperation {
- input: InputObject,
- z: Option<f64>,
- origin: Option<Point>,
- pen: Option<u32>,
- layer: Option<usize>,
- array: Option<ArrayConfig>,
- hatch: Option<HatchConfig>,
- export: Option<PathBuf>,
- replace_object: Option<usize>,
- }
- impl ObjectOperation {
- pub fn process(&self, pens: &Vec<Pen>, layers: &mut ArrayOf<Layer>) {
- debug!("Processing object {}", self.input);
- let mut object: Object = self.input.new(layers);
- // Process basic transformation
- object.translate(self.origin, self.z);
- self.pen.map(|pen| object.set_pen(pen));
- // Process conversion to hatch object
- // Process array generation of object
- let new_objects = self.array.as_ref().map_or(vec![object.clone()], |array| {
- let bottom_left: Point = Point {
- x: (array.columns - 1) as f64 * array.spacing / -2.0,
- y: (array.rows - 1) as f64 * array.spacing / -2.0,
- };
- // Closure that returns origin point of given index in array, where index starts
- // from bottom left and increments to the right and wraps around to the row above
- let calc_pt = |index: usize| {
- let x_pos: f64 = (index % array.columns) as f64;
- let y_pos: f64 = (index / array.columns) as f64;
- Point {
- x: bottom_left.x + array.spacing * x_pos,
- y: bottom_left.y + array.spacing * y_pos,
- }
- };
- // Randomize draw order
- let mut seq: Vec<usize> = (0..(array.rows * array.columns)).collect();
- if array.randomize_order {
- seq.shuffle(&mut thread_rng());
- }
- // Generate objects
- let mut new_obj: Vec<Object> = vec![];
- for obj_idx in seq.into_iter() {
- let mut object: Object = object.clone();
- object.translate(Some(calc_pt(obj_idx)), None);
- object.set_pen(array.starting_pen + u32::try_from(obj_idx).unwrap());
- new_obj.push(object);
- }
- new_obj
- });
- // Process export of object
- self.export.as_ref().map(|path| {
- if new_objects.len() > 1 {
- warn!("Exporting only the first object in list of objects");
- } else {
- debug!(
- "Exporting object {} to '{}'",
- new_objects[0],
- path.to_string_lossy()
- );
- }
- new_objects[0].write_to_file(path);
- });
- // Append or replace object in layer
- let layer: &mut Layer = layers
- .get_mut(self.layer.unwrap_or(0))
- .expect("Invalid layer index");
- match self.replace_object {
- Some(object) => {
- // debug!("Replacing object #{} with {}", object, new_objects);
- layer.objects.splice(object..=object, new_objects);
- }
- None => {
- layer.objects.extend(new_objects);
- }
- }
- }
- }
- // #[derive(Debug, Serialize, Deserialize)]
- // #[serde(rename_all = "PascalCase")]
- // pub struct Array {
- // layer: usize,
- // z: f64,
- // columns: usize,
- // rows: usize,
- // spacing: f64,
- // starting_pen: usize,
- // object: InputObject,
- // hatch: Option<HatchConfig>,
- // }
- // impl Array {
- // pub fn generate(&self, pens: &Vec<Pen>, layers: &mut ArrayOf<Layer>) {
- // debug!(
- // "Generating {} x {} array of {:?} with spacing of {} starting from pen #{} on layer #{}",
- // self.columns, self.rows, self.object, self.spacing, self.starting_pen, self.layer
- // );
- // assert!(
- // self.rows >= 1 && self.columns >= 1,
- // "Invalid row/column value"
- // );
- // assert!(
- // self.starting_pen + (self.rows * self.columns) < pens.len(),
- // "Invalid starting pen"
- // );
- // let layer: &mut Layer = layers.get_mut(self.layer).expect("Invalid layer index");
- // let bottom_left: Point = Point {
- // x: (self.columns - 1) as f64 * self.spacing / -2.0,
- // y: (self.rows - 1) as f64 * self.spacing / -2.0,
- // };
- // // Closure that returns origin point of given index in array, where index starts
- // // from bottom left and increments to the right and wraps around to the row above
- // let calc_pt = |index: usize| {
- // let x_pos: f64 = (index % self.columns) as f64;
- // let y_pos: f64 = (index / self.columns) as f64;
- // Point {
- // x: bottom_left.x + self.spacing * x_pos,
- // y: bottom_left.y + self.spacing * y_pos,
- // }
- // };
- // // Randomize draw order
- // let mut seq: Vec<usize> = (0..(self.rows * self.columns)).collect();
- // seq.shuffle(&mut thread_rng());
- // // Generate objects and append to layer
- // for obj_idx in seq.into_iter() {
- // let pen: u32 = (self.starting_pen + obj_idx).try_into().unwrap();
- // // // Build outline object
- // // let mut rect: Rectangle = Rectangle::default();
- // // let origin: Point = calc_pt(obj_idx);
- // // *rect.corner_a = Point {
- // // x: origin.x - self.width / 2.0,
- // // y: origin.y - self.height / 2.0,
- // // };
- // // *rect.corner_b = Point {
- // // x: origin.x + self.width / 2.0,
- // // y: origin.y + self.height / 2.0,
- // // };
- // // *rect.core.origin = origin;
- // // *rect.core.pen = pen;
- // // *rect.core.z = self.z;
- // // debug!(
- // // "Adding hatched rectangle with pen #{} at {} (from {} to {})",
- // // *rect.core.pen, *rect.core.origin, *rect.corner_a, *rect.corner_b
- // // );
- // // let mut hatch_setting: HatchSetting = self.hatch.into();
- // // *hatch_setting.pen = pen;
- // // // Build hatch object
- // // let hatch: Hatch = Hatch {
- // // core: ObjectCore {
- // // origin: origin.into(),
- // // z: self.z.into(),
- // // ..ObjectCore::default(ObjectType::Hatch)
- // // },
- // // outline: vec![Object::Rectangle(rect)].into(),
- // // legacy_setting: vec![hatch_setting.clone()].into(),
- // // hatch_settings: vec![hatch_setting.clone()].into(),
- // // hatches: Some(Hatches {
- // // core: ObjectCore::default(ObjectType::HatchLine),
- // // hatch_lines: vec![].into(),
- // // }),
- // // };
- // // layer.objects.push(Object::Hatch(hatch));
- // }
- // }
- // }
|