object.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. use std::path::PathBuf;
  2. use ezcad::{
  3. array_of::ArrayOf,
  4. layer::Layer,
  5. objects::{
  6. circle::Circle,
  7. hatch::{Hatch, HatchFlag, HatchPattern, HatchSetting, Hatches},
  8. rectangle::Rectangle,
  9. Object, ObjectCore, Translate,
  10. },
  11. pen::Pen,
  12. types::{Coordinate, ObjectType},
  13. };
  14. use log::{debug, error, warn};
  15. use rand::{seq::SliceRandom, thread_rng};
  16. use serde::{Deserialize, Serialize};
  17. #[derive(Debug, Serialize, Deserialize)]
  18. #[serde(rename_all = "PascalCase")]
  19. pub struct DeleteObjects {
  20. layer: usize,
  21. object: Option<usize>,
  22. }
  23. impl DeleteObjects {
  24. pub fn delete(&self, layers: &mut ArrayOf<Layer>) {
  25. debug!(
  26. "Deleting layer #{} {}",
  27. match &self.object {
  28. Some(object) => format!("object #{}", object),
  29. None => format!("all objects"),
  30. },
  31. self.layer,
  32. );
  33. let layer: &mut Layer = layers.get_mut(self.layer).expect("Invalid layer index");
  34. match self.object {
  35. Some(index) => {
  36. assert!(index < layer.objects.len(), "Invalid object index");
  37. layer.objects.remove(index);
  38. }
  39. None => layer.objects.clear(),
  40. }
  41. }
  42. }
  43. #[derive(Copy, Clone, Debug, Serialize, Deserialize)]
  44. #[serde(rename_all = "PascalCase")]
  45. pub struct HatchConfig {
  46. line_spacing: f64,
  47. pen: Option<u32>,
  48. count: Option<u32>,
  49. edge_offset: Option<f64>,
  50. start_offset: Option<f64>,
  51. end_offset: Option<f64>,
  52. angle: Option<f64>,
  53. rotate_angle: Option<f64>,
  54. line_reduction: Option<f64>,
  55. loop_distance: Option<f64>,
  56. loop_count: Option<u32>,
  57. pattern: Option<HatchPattern>,
  58. follow_edge_once: Option<bool>,
  59. cross_hatch: Option<bool>,
  60. }
  61. impl From<HatchConfig> for HatchSetting {
  62. fn from(value: HatchConfig) -> Self {
  63. let mut flags: HatchFlag = match value.pattern {
  64. Some(pattern) => pattern.into(),
  65. None => HatchPattern::Directional.into(),
  66. };
  67. value
  68. .follow_edge_once
  69. .map(|x| flags.set_follow_edge_once(x.into()));
  70. value.cross_hatch.map(|x| flags.set_cross_hatch(x.into()));
  71. if value.start_offset.is_none() && value.end_offset.is_none() {
  72. flags.set_average_distribute_line(1);
  73. }
  74. let mut ret = Self::default();
  75. *ret.line_spacing = value.line_spacing;
  76. value.pen.map(|x| *ret.pen = x.into());
  77. value.count.map(|x| *ret.count = x.into());
  78. value.edge_offset.map(|x| *ret.edge_offset = x.into());
  79. value.start_offset.map(|x| *ret.start_offset = x.into());
  80. value.end_offset.map(|x| *ret.end_offset = x.into());
  81. value.angle.map(|x| *ret.angle = x.into());
  82. value.rotate_angle.map(|x| *ret.rotate_angle = x.into());
  83. value.line_reduction.map(|x| *ret.line_reduction = x.into());
  84. value.loop_distance.map(|x| *ret.loop_distance = x.into());
  85. value.loop_count.map(|x| *ret.loop_count = x.into());
  86. *ret.flags = flags;
  87. *ret.enabled = true.into();
  88. ret
  89. }
  90. }
  91. #[derive(Debug, Serialize, Deserialize)]
  92. #[serde(rename_all = "PascalCase")]
  93. pub struct ArrayConfig {
  94. columns: usize,
  95. rows: usize,
  96. spacing: f64,
  97. randomize_order: bool,
  98. starting_pen: u32,
  99. }
  100. #[derive(Debug, Serialize, Deserialize, strum::Display)]
  101. #[serde(rename_all = "PascalCase")]
  102. pub enum InputObject {
  103. #[serde(rename_all = "PascalCase")]
  104. Rectangle {
  105. width: f64,
  106. height: f64,
  107. round_corner: Option<f64>,
  108. },
  109. #[serde(rename_all = "PascalCase")]
  110. Circle { radius: f64 },
  111. #[serde(rename_all = "PascalCase")]
  112. Import { path: PathBuf },
  113. #[serde(rename_all = "PascalCase")]
  114. Existing { layer: usize, object: usize },
  115. }
  116. impl InputObject {
  117. fn new(&self, layers: &ArrayOf<Layer>) -> Object {
  118. match self {
  119. InputObject::Rectangle {
  120. width,
  121. height,
  122. round_corner,
  123. } => Object::Rectangle(Rectangle {
  124. drawn_corner_a: Coordinate::from((-width / 2.0, -height / 2.0)).into(),
  125. drawn_corner_b: Coordinate::from((width / 2.0, height / 2.0)).into(),
  126. round_bottom_left: round_corner.unwrap_or(0.0).into(),
  127. round_bottom_right: round_corner.unwrap_or(0.0).into(),
  128. round_top_left: round_corner.unwrap_or(0.0).into(),
  129. round_top_right: round_corner.unwrap_or(0.0).into(),
  130. ..Default::default()
  131. }),
  132. InputObject::Circle { radius } => Object::Circle(Circle {
  133. radius: (*radius).into(),
  134. ..Default::default()
  135. }),
  136. InputObject::Import { path } => Object::read_from_file(path),
  137. InputObject::Existing { layer, object } => {
  138. let layer: &Layer = layers.get(*layer).expect("Invalid layer index");
  139. layer
  140. .objects
  141. .get(*object)
  142. .expect("Invalid object index")
  143. .clone()
  144. }
  145. }
  146. }
  147. }
  148. #[derive(Debug, Serialize, Deserialize)]
  149. #[serde(rename_all = "PascalCase")]
  150. pub struct ObjectOperation {
  151. input: InputObject,
  152. z: Option<f64>,
  153. origin: Option<Coordinate>,
  154. pen: Option<u32>,
  155. layer: Option<usize>,
  156. array: Option<ArrayConfig>,
  157. hatch: Option<HatchConfig>,
  158. export: Option<PathBuf>,
  159. replace_object: Option<usize>,
  160. }
  161. impl ObjectOperation {
  162. pub fn process(&self, pens: &Vec<Pen>, layers: &mut ArrayOf<Layer>) {
  163. debug!("Begin processing of object {:?}", self.input);
  164. let mut object: Object = self.input.new(layers);
  165. // Process basic transformation
  166. if self.origin.is_some() || self.z.is_some() {
  167. debug!(
  168. "Translating object to origin {:?} and Z {:?}",
  169. self.origin, self.z
  170. );
  171. object.move_absolute(self.origin, self.z);
  172. }
  173. self.pen.map(|pen| {
  174. if self.array.is_some() {
  175. warn!("Ignoring pen setting as values will be overridden by array setting");
  176. } else {
  177. assert!(pen < pens.len().try_into().unwrap(), "Invalid pen index");
  178. debug!("Setting object pen to #{}", pen);
  179. object.set_pen(pen);
  180. }
  181. });
  182. // Process conversion to hatch object
  183. let object = self.hatch.as_ref().map_or(object.clone(), |hatch| {
  184. if self.array.is_some() {
  185. warn!("Ignoring hatch pen setting as values will be overridden by array setting");
  186. }
  187. let hatch_setting: HatchSetting = (*hatch).into();
  188. match object {
  189. Object::Curve(_) | Object::Point(_) => {
  190. error!("Points, lines, and curves cannot be hatched");
  191. object
  192. }
  193. Object::Rectangle(_)
  194. | Object::Circle(_)
  195. | Object::Ellipse(_)
  196. | Object::Polygon(_) => {
  197. debug!("Converting to hatch object");
  198. // Build new hatch object (no hatch lines)
  199. Object::Hatch(Hatch {
  200. core: ObjectCore {
  201. origin: object.core().origin,
  202. z: object.core().z,
  203. ..ObjectCore::default(ObjectType::Hatch)
  204. },
  205. outline: vec![object].into(),
  206. legacy_setting: vec![hatch_setting.clone()].into(),
  207. hatch_settings: vec![hatch_setting.clone()].into(),
  208. hatches: Some(Hatches {
  209. core: ObjectCore::default(ObjectType::HatchLine),
  210. hatch_lines: vec![].into(),
  211. }),
  212. })
  213. }
  214. Object::Hatch(hatch) => {
  215. debug!("Modifying existing hatch settings");
  216. // Modify existing hatch (delete existing hatch lines)
  217. Object::Hatch(Hatch {
  218. legacy_setting: vec![hatch_setting.clone()].into(),
  219. hatch_settings: vec![hatch_setting.clone()].into(),
  220. hatches: Some(Hatches {
  221. core: ObjectCore::default(ObjectType::HatchLine),
  222. hatch_lines: vec![].into(),
  223. }),
  224. ..hatch
  225. })
  226. }
  227. }
  228. });
  229. // Process array generation of object
  230. let new_objects = self.array.as_ref().map_or(vec![object.clone()], |array| {
  231. let bottom_left: Coordinate = Coordinate {
  232. x: (array.columns - 1) as f64 * array.spacing / -2.0,
  233. y: (array.rows - 1) as f64 * array.spacing / -2.0,
  234. };
  235. // Closure that returns origin point of given index in array, where index starts
  236. // from bottom left and increments to the right and wraps around to the row above
  237. let calc_pt = |index: usize| {
  238. let x_pos: f64 = (index % array.columns) as f64;
  239. let y_pos: f64 = (index / array.columns) as f64;
  240. Coordinate {
  241. x: bottom_left.x + array.spacing * x_pos,
  242. y: bottom_left.y + array.spacing * y_pos,
  243. }
  244. };
  245. // Randomize draw order
  246. let mut seq: Vec<usize> = (0..(array.rows * array.columns)).collect();
  247. if array.randomize_order {
  248. seq.shuffle(&mut thread_rng());
  249. }
  250. // Generate objects
  251. let mut new_obj: Vec<Object> = vec![];
  252. for obj_idx in seq.into_iter() {
  253. let delta: Coordinate = calc_pt(obj_idx);
  254. let pen: u32 = array.starting_pen + u32::try_from(obj_idx).unwrap();
  255. let mut object: Object = object.clone();
  256. object.move_relative(Some(delta), None);
  257. object.set_pen(pen);
  258. debug!(
  259. "Adding new array instance at {} with pen #{}",
  260. object.core().origin,
  261. pen
  262. );
  263. new_obj.push(object);
  264. }
  265. new_obj
  266. });
  267. let layer_id: usize = self.layer.unwrap_or(0);
  268. let layer: &mut Layer = layers.get_mut(layer_id).expect("Invalid layer index");
  269. // Either export the object, replace an existing object, or append if neither
  270. if let Some(path) = &self.export {
  271. if new_objects.len() > 1 {
  272. warn!("Exporting only the first object in list of objects");
  273. } else {
  274. debug!(
  275. "Exporting object {} in layer #{} to '{}'",
  276. new_objects[0],
  277. layer_id,
  278. path.to_string_lossy()
  279. );
  280. }
  281. new_objects[0].write_to_file(path);
  282. } else if let Some(object) = self.replace_object {
  283. assert!(object < layer.objects.len(), "Invalid object index");
  284. debug!(
  285. "Replacing object #{} in layer #{} with new objects",
  286. object, layer_id
  287. );
  288. layer.objects.splice(object..=object, new_objects);
  289. } else {
  290. debug!("Extending layer #{} with new objects", layer_id);
  291. layer.objects.extend(new_objects);
  292. }
  293. }
  294. }