object.rs 12 KB

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