|
@@ -1,10 +1,5 @@
|
|
|
-use std::{
|
|
|
- fs::File,
|
|
|
- io::{Cursor, Read, Write},
|
|
|
- path::PathBuf,
|
|
|
-};
|
|
|
+use std::path::PathBuf;
|
|
|
|
|
|
-use binrw::{BinRead, BinWriterExt};
|
|
|
use ezcad::{
|
|
|
array_of::ArrayOf,
|
|
|
layer::Layer,
|
|
@@ -15,11 +10,11 @@ use ezcad::{
|
|
|
Object, ObjectCore, Translate,
|
|
|
},
|
|
|
pen::Pen,
|
|
|
- types::{ObjectType, Point},
|
|
|
+ types::{ObjectType, Coordinate},
|
|
|
};
|
|
|
-use log::{debug, warn};
|
|
|
+use log::{debug, error, warn};
|
|
|
use rand::{seq::SliceRandom, thread_rng};
|
|
|
-use serde::{de, Deserialize, Serialize};
|
|
|
+use serde::{Deserialize, Serialize};
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
#[serde(rename_all = "PascalCase")]
|
|
@@ -56,6 +51,7 @@ impl DeleteObjects {
|
|
|
pub struct HatchConfig {
|
|
|
line_spacing: f64,
|
|
|
|
|
|
+ pen: Option<u32>,
|
|
|
count: Option<u32>,
|
|
|
edge_offset: Option<f64>,
|
|
|
start_offset: Option<f64>,
|
|
@@ -90,6 +86,7 @@ impl From<HatchConfig> for HatchSetting {
|
|
|
let mut ret = Self::default();
|
|
|
|
|
|
*ret.line_spacing = value.line_spacing;
|
|
|
+ value.pen.map(|x| *ret.pen = x.into());
|
|
|
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());
|
|
@@ -120,21 +117,18 @@ pub struct ArrayConfig {
|
|
|
#[derive(Debug, Serialize, Deserialize, strum::Display)]
|
|
|
#[serde(rename_all = "PascalCase")]
|
|
|
pub enum InputObject {
|
|
|
+ #[serde(rename_all = "PascalCase")]
|
|
|
Rectangle {
|
|
|
width: f64,
|
|
|
height: f64,
|
|
|
round_corner: Option<f64>,
|
|
|
},
|
|
|
- Circle {
|
|
|
- radius: f64,
|
|
|
- },
|
|
|
- Import {
|
|
|
- path: PathBuf,
|
|
|
- },
|
|
|
- Existing {
|
|
|
- layer: usize,
|
|
|
- object: usize,
|
|
|
- },
|
|
|
+ #[serde(rename_all = "PascalCase")]
|
|
|
+ Circle { radius: f64 },
|
|
|
+ #[serde(rename_all = "PascalCase")]
|
|
|
+ Import { path: PathBuf },
|
|
|
+ #[serde(rename_all = "PascalCase")]
|
|
|
+ Existing { layer: usize, object: usize },
|
|
|
}
|
|
|
|
|
|
impl InputObject {
|
|
@@ -145,8 +139,8 @@ impl InputObject {
|
|
|
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(),
|
|
|
+ corner_a: Coordinate::from((-width / 2.0, -height / 2.0)).into(),
|
|
|
+ corner_b: Coordinate::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(),
|
|
@@ -175,7 +169,7 @@ impl InputObject {
|
|
|
pub struct ObjectOperation {
|
|
|
input: InputObject,
|
|
|
z: Option<f64>,
|
|
|
- origin: Option<Point>,
|
|
|
+ origin: Option<Coordinate>,
|
|
|
pen: Option<u32>,
|
|
|
layer: Option<usize>,
|
|
|
array: Option<ArrayConfig>,
|
|
@@ -186,19 +180,81 @@ pub struct ObjectOperation {
|
|
|
|
|
|
impl ObjectOperation {
|
|
|
pub fn process(&self, pens: &Vec<Pen>, layers: &mut ArrayOf<Layer>) {
|
|
|
- debug!("Processing object {}", self.input);
|
|
|
+ debug!("Begin processing of 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));
|
|
|
+ if self.origin.is_some() || self.z.is_some() {
|
|
|
+ debug!(
|
|
|
+ "Translating object to origin {:?} and Z {:?}",
|
|
|
+ self.origin, self.z
|
|
|
+ );
|
|
|
+ object.move_absolute(self.origin, self.z);
|
|
|
+ }
|
|
|
+
|
|
|
+ self.pen.map(|pen| {
|
|
|
+ if self.array.is_some() {
|
|
|
+ warn!("Ignoring pen setting as values will be overridden by array setting");
|
|
|
+ } else {
|
|
|
+ assert!(pen < pens.len().try_into().unwrap(), "Invalid pen index");
|
|
|
+ debug!("Setting object pen to #{}", pen);
|
|
|
+ object.set_pen(pen);
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
// Process conversion to hatch object
|
|
|
+ let object = self.hatch.as_ref().map_or(object.clone(), |hatch| {
|
|
|
+ if self.array.is_some() {
|
|
|
+ warn!("Ignoring hatch pen setting as values will be overridden by array setting");
|
|
|
+ }
|
|
|
+ let hatch_setting: HatchSetting = (*hatch).into();
|
|
|
+
|
|
|
+ match object {
|
|
|
+ Object::Curve(_) | Object::Point(_) => {
|
|
|
+ error!("Points, lines, and curves cannot be hatched");
|
|
|
+ object
|
|
|
+ }
|
|
|
+ Object::Rectangle(_)
|
|
|
+ | Object::Circle(_)
|
|
|
+ | Object::Ellipse(_)
|
|
|
+ | Object::Polygon(_) => {
|
|
|
+ debug!("Converting to hatch object");
|
|
|
+ // Build new hatch object (no hatch lines)
|
|
|
+ Object::Hatch(Hatch {
|
|
|
+ core: ObjectCore {
|
|
|
+ origin: object.core().origin,
|
|
|
+ z: object.core().z,
|
|
|
+ ..ObjectCore::default(ObjectType::Hatch)
|
|
|
+ },
|
|
|
+ outline: vec![object].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(),
|
|
|
+ }),
|
|
|
+ })
|
|
|
+ }
|
|
|
+ Object::Hatch(hatch) => {
|
|
|
+ debug!("Modifying existing hatch settings");
|
|
|
+ // Modify existing hatch (delete existing hatch lines)
|
|
|
+ Object::Hatch(Hatch {
|
|
|
+ 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(),
|
|
|
+ }),
|
|
|
+ ..hatch
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
|
|
|
// Process array generation of object
|
|
|
let new_objects = self.array.as_ref().map_or(vec![object.clone()], |array| {
|
|
|
- let bottom_left: Point = Point {
|
|
|
+ let bottom_left: Coordinate = Coordinate {
|
|
|
x: (array.columns - 1) as f64 * array.spacing / -2.0,
|
|
|
y: (array.rows - 1) as f64 * array.spacing / -2.0,
|
|
|
};
|
|
@@ -209,7 +265,7 @@ impl ObjectOperation {
|
|
|
let x_pos: f64 = (index % array.columns) as f64;
|
|
|
let y_pos: f64 = (index / array.columns) as f64;
|
|
|
|
|
|
- Point {
|
|
|
+ Coordinate {
|
|
|
x: bottom_left.x + array.spacing * x_pos,
|
|
|
y: bottom_left.y + array.spacing * y_pos,
|
|
|
}
|
|
@@ -225,142 +281,50 @@ impl ObjectOperation {
|
|
|
let mut new_obj: Vec<Object> = vec![];
|
|
|
|
|
|
for obj_idx in seq.into_iter() {
|
|
|
+ let delta: Coordinate = calc_pt(obj_idx);
|
|
|
+ let pen: u32 = array.starting_pen + u32::try_from(obj_idx).unwrap();
|
|
|
+
|
|
|
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());
|
|
|
+ object.move_relative(Some(delta), None);
|
|
|
+ object.set_pen(pen);
|
|
|
|
|
|
+ debug!(
|
|
|
+ "Adding new array instance at {} with pen #{}",
|
|
|
+ object.core().origin,
|
|
|
+ pen
|
|
|
+ );
|
|
|
new_obj.push(object);
|
|
|
}
|
|
|
|
|
|
new_obj
|
|
|
});
|
|
|
|
|
|
- // Process export of object
|
|
|
- self.export.as_ref().map(|path| {
|
|
|
+ let layer_id: usize = self.layer.unwrap_or(0);
|
|
|
+ let layer: &mut Layer = layers.get_mut(layer_id).expect("Invalid layer index");
|
|
|
+
|
|
|
+ // Either export the object, replace an existing object, or append if neither
|
|
|
+ if let Some(path) = &self.export {
|
|
|
if new_objects.len() > 1 {
|
|
|
warn!("Exporting only the first object in list of objects");
|
|
|
} else {
|
|
|
debug!(
|
|
|
- "Exporting object {} to '{}'",
|
|
|
+ "Exporting object {} in layer #{} to '{}'",
|
|
|
new_objects[0],
|
|
|
+ layer_id,
|
|
|
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);
|
|
|
- }
|
|
|
+ } else if let Some(object) = self.replace_object {
|
|
|
+ assert!(object < layer.objects.len(), "Invalid object index");
|
|
|
+ debug!(
|
|
|
+ "Replacing object #{} in layer #{} with new objects",
|
|
|
+ object, layer_id
|
|
|
+ );
|
|
|
+ layer.objects.splice(object..=object, new_objects);
|
|
|
+ } else {
|
|
|
+ debug!("Extending layer #{} with new objects", layer_id);
|
|
|
+ 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));
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|