hatch.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. use std::fmt::{Debug, Display};
  2. use crate::{
  3. array_of::ArrayOf,
  4. field_of::FieldOf,
  5. types::{Coordinate, Field, F64, U32},
  6. };
  7. use binrw::{binrw, BinRead, BinWrite};
  8. use diff::Diff;
  9. use modular_bitfield::{
  10. bitfield,
  11. specifiers::{B1, B18},
  12. };
  13. use serde::{Deserialize, Serialize};
  14. use super::{line::Lines, Object, ObjectCore, Translate};
  15. #[bitfield(bits = 32)]
  16. #[derive(BinRead, BinWrite, Copy, Clone, Debug, Default, Diff, PartialEq)]
  17. #[diff(attr(
  18. #[derive(Debug, PartialEq)]
  19. ))]
  20. #[br(map = Self::from_bytes)]
  21. #[bw(map = |&x| Self::into_bytes(x))]
  22. pub struct HatchFlag {
  23. pub all_calc: B1,
  24. pub follow_edge_once: B1,
  25. pub continuous_pattern: B1, // Hatch type 3 when combined with bidirectional_pattern
  26. pub bidirectional_pattern: B1, // Hatch type 1
  27. pub ring_pattern: B1, // Hatch type 2
  28. #[skip]
  29. __: B1,
  30. pub auto_rotate_hatch_angle: B1,
  31. pub average_distribute_line: B1,
  32. #[skip]
  33. __: B1,
  34. pub gong_pattern: B1, // Hatch type 4 when combined with bidirectional_pattern
  35. pub cross_hatch: B1,
  36. pub background_pattern: B1, // Hatch type 5, must set all_calc as well
  37. pub fill_pattern: B1, // Hatch type 6 when combined with continuous_pattern and bidirectional_pattern
  38. pub zigzag_pattern: B1, // Hatch type 7
  39. #[skip]
  40. __: B18,
  41. }
  42. #[derive(Copy, Clone, Debug, Serialize, Deserialize, strum::Display)]
  43. pub enum HatchPattern {
  44. Directional,
  45. Bidirectional,
  46. Ring,
  47. Continuous,
  48. Gong,
  49. Background,
  50. Fill,
  51. Zigzag,
  52. }
  53. impl From<HatchPattern> for HatchFlag {
  54. fn from(value: HatchPattern) -> Self {
  55. let mut ret = Self::new();
  56. match value {
  57. HatchPattern::Directional => (),
  58. HatchPattern::Bidirectional => ret.set_bidirectional_pattern(1),
  59. HatchPattern::Ring => ret.set_ring_pattern(1),
  60. HatchPattern::Continuous => {
  61. ret.set_bidirectional_pattern(1);
  62. ret.set_continuous_pattern(1);
  63. }
  64. HatchPattern::Gong => {
  65. ret.set_bidirectional_pattern(1);
  66. ret.set_gong_pattern(1);
  67. }
  68. HatchPattern::Background => {
  69. ret.set_background_pattern(1);
  70. ret.set_all_calc(1);
  71. }
  72. HatchPattern::Fill => ret.set_fill_pattern(1),
  73. HatchPattern::Zigzag => ret.set_zigzag_pattern(1),
  74. }
  75. ret
  76. }
  77. }
  78. impl From<HatchFlag> for HatchPattern {
  79. fn from(value: HatchFlag) -> Self {
  80. if value.ring_pattern() != 0 {
  81. HatchPattern::Ring
  82. } else if value.background_pattern() != 0 {
  83. HatchPattern::Background
  84. } else if value.fill_pattern() != 0 {
  85. HatchPattern::Fill
  86. } else if value.zigzag_pattern() != 0 {
  87. HatchPattern::Zigzag
  88. } else if value.gong_pattern() != 0 {
  89. HatchPattern::Gong
  90. } else if value.bidirectional_pattern() != 0 {
  91. if value.continuous_pattern() != 0 {
  92. HatchPattern::Continuous
  93. } else {
  94. HatchPattern::Bidirectional
  95. }
  96. } else {
  97. HatchPattern::Directional
  98. }
  99. }
  100. }
  101. #[cfg_attr(feature = "default-debug", derive(Debug))]
  102. #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
  103. #[diff(attr(
  104. #[derive(Debug, PartialEq)]
  105. ))]
  106. // #[brw(magic(46u32))] // Number of fields in this struct
  107. #[brw(magic(47u32))] // Number of fields in this struct
  108. pub struct LegacyHatchSetting {
  109. pub mark_contour: U32,
  110. pub hatch_0_enable: U32,
  111. pub hatch_0_pen: U32,
  112. pub hatch_0_flags: FieldOf<HatchFlag>,
  113. pub hatch_0_edge_offset: F64,
  114. pub hatch_0_line_spacing: F64,
  115. pub hatch_0_start_offset: F64,
  116. pub hatch_0_end_offset: F64,
  117. pub hatch_0_angle: F64,
  118. pub hatch_1_enable: U32,
  119. pub hatch_1_pen: U32,
  120. pub hatch_1_flags: FieldOf<HatchFlag>,
  121. pub hatch_1_edge_offset: F64,
  122. pub hatch_1_line_spacing: F64,
  123. pub hatch_1_start_offset: F64,
  124. pub hatch_1_end_offset: F64,
  125. pub hatch_1_angle: F64,
  126. pub any_hatch_enabled: U32,
  127. pub hatch_0_rotate_angle: F64,
  128. pub hatch_1_rotate_angle: F64,
  129. pub hatch_2_enable: U32,
  130. pub hatch_2_pen: U32,
  131. pub hatch_2_flags: FieldOf<HatchFlag>,
  132. pub hatch_2_edge_offset: F64,
  133. pub hatch_2_line_spacing: F64,
  134. pub hatch_2_start_offset: F64,
  135. pub hatch_2_end_offset: F64,
  136. pub hatch_2_angle: F64,
  137. pub hatch_2_rotate_angle: F64,
  138. pub hatch_0_line_reduction: F64,
  139. pub hatch_1_line_reduction: F64,
  140. pub hatch_2_line_reduction: F64,
  141. pub hatch_0_loop_count: U32,
  142. pub hatch_1_loop_count: U32,
  143. pub hatch_2_loop_count: U32,
  144. pub hatch_0_loop_distance: F64,
  145. pub hatch_1_loop_distance: F64,
  146. pub hatch_2_loop_distance: F64,
  147. pub _unknown_1: [Field; 3],
  148. pub contour_priority: U32,
  149. pub hatch_0_count: U32,
  150. pub hatch_1_count: U32,
  151. pub hatch_2_count: U32,
  152. }
  153. // Custom Debug implementation to only print known fields
  154. #[cfg(not(feature = "default-debug"))]
  155. impl Debug for LegacyHatchSetting {
  156. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  157. f.debug_struct("LegacyHatchSetting")
  158. .field("mark_contour", &self.mark_contour)
  159. .field("hatch_0_enable", &self.hatch_0_enable)
  160. .field("hatch_0_pen", &self.hatch_0_pen)
  161. .field("hatch_0_flags", &self.hatch_0_flags)
  162. .field("hatch_0_edge_offset", &self.hatch_0_edge_offset)
  163. .field("hatch_0_line_spacing", &self.hatch_0_line_spacing)
  164. .field("hatch_0_start_offset", &self.hatch_0_start_offset)
  165. .field("hatch_0_end_offset", &self.hatch_0_end_offset)
  166. .field("hatch_0_angle", &self.hatch_0_angle)
  167. .field("hatch_1_enable", &self.hatch_1_enable)
  168. .field("hatch_1_pen", &self.hatch_1_pen)
  169. .field("hatch_1_flags", &self.hatch_1_flags)
  170. .field("hatch_1_edge_offset", &self.hatch_1_edge_offset)
  171. .field("hatch_1_line_spacing", &self.hatch_1_line_spacing)
  172. .field("hatch_1_start_offset", &self.hatch_1_start_offset)
  173. .field("hatch_1_end_offset", &self.hatch_1_end_offset)
  174. .field("hatch_1_angle", &self.hatch_1_angle)
  175. .field("any_hatch_enabled", &self.any_hatch_enabled)
  176. .field("hatch_0_auto_rotate_angle", &self.hatch_0_rotate_angle)
  177. .field("hatch_1_auto_rotate_angle", &self.hatch_1_rotate_angle)
  178. .field("hatch_2_enable", &self.hatch_2_enable)
  179. .field("hatch_2_pen", &self.hatch_2_pen)
  180. .field("hatch_2_flags", &self.hatch_2_flags)
  181. .field("hatch_2_edge_offset", &self.hatch_2_edge_offset)
  182. .field("hatch_2_line_spacing", &self.hatch_2_line_spacing)
  183. .field("hatch_2_start_offset", &self.hatch_2_start_offset)
  184. .field("hatch_2_end_offset", &self.hatch_2_end_offset)
  185. .field("hatch_2_angle", &self.hatch_2_angle)
  186. .field("hatch_2_auto_rotate_angle", &self.hatch_2_rotate_angle)
  187. .field("hatch_0_line_reduction", &self.hatch_0_line_reduction)
  188. .field("hatch_1_line_reduction", &self.hatch_1_line_reduction)
  189. .field("hatch_2_line_reduction", &self.hatch_2_line_reduction)
  190. .field("hatch_0_loop_count", &self.hatch_0_loop_count)
  191. .field("hatch_1_loop_count", &self.hatch_1_loop_count)
  192. .field("hatch_2_loop_count", &self.hatch_2_loop_count)
  193. .field("hatch_0_loop_distance", &self.hatch_0_loop_distance)
  194. .field("hatch_1_loop_distance", &self.hatch_1_loop_distance)
  195. .field("hatch_2_loop_distance", &self.hatch_2_loop_distance)
  196. .field("contour_priority", &self.contour_priority)
  197. .field("hatch_0_count", &self.hatch_0_count)
  198. .field("hatch_1_count", &self.hatch_1_count)
  199. .field("hatch_2_count", &self.hatch_2_count)
  200. .finish()
  201. }
  202. }
  203. #[cfg_attr(feature = "default-debug", derive(Debug))]
  204. #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
  205. #[diff(attr(
  206. #[derive(Debug, PartialEq)]
  207. ))]
  208. #[brw(magic(15u32))] // Number of fields in this struct
  209. pub struct HatchSetting {
  210. pub count: U32,
  211. pub enabled: U32,
  212. pub pen: U32,
  213. pub flags: FieldOf<HatchFlag>,
  214. pub edge_offset: F64,
  215. pub line_spacing: F64,
  216. pub start_offset: F64,
  217. pub end_offset: F64,
  218. pub angle: F64,
  219. pub rotate_angle: F64,
  220. pub line_reduction: F64,
  221. pub loop_distance: F64,
  222. pub loop_count: U32,
  223. pub _unknown_1: [Field; 2],
  224. }
  225. // Custom Debug implementation to only print known fields
  226. #[cfg(not(feature = "default-debug"))]
  227. impl Debug for HatchSetting {
  228. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  229. f.debug_struct("HatchSettings")
  230. .field("count", &self.count)
  231. .field("enabled", &self.enabled)
  232. .field("pen", &self.pen)
  233. .field("flags", &self.flags)
  234. .field("edge_offset", &self.edge_offset)
  235. .field("line_spacing", &self.line_spacing)
  236. .field("start_offset", &self.start_offset)
  237. .field("end_offset", &self.end_offset)
  238. .field("angle", &self.angle)
  239. .field("auto_rotate_angle", &self.rotate_angle)
  240. .field("line_reduction", &self.line_reduction)
  241. .field("loop_distance", &self.loop_distance)
  242. .field("loop_count", &self.loop_count)
  243. .finish()
  244. }
  245. }
  246. impl Default for HatchSetting {
  247. fn default() -> Self {
  248. Self {
  249. count: 1.into(),
  250. enabled: false.into(),
  251. pen: 0.into(),
  252. flags: HatchFlag::new().into(),
  253. edge_offset: 0.0.into(),
  254. line_spacing: 1.0.into(),
  255. start_offset: 0.0.into(),
  256. end_offset: 0.0.into(),
  257. angle: 0.0.into(),
  258. rotate_angle: 10.0.into(),
  259. line_reduction: 0.0.into(),
  260. loop_distance: 0.5.into(),
  261. loop_count: 0.into(),
  262. _unknown_1: [vec![0, 0, 0, 0, 0, 0, 0, 0].into(), vec![0, 0, 0, 0].into()].into(),
  263. }
  264. }
  265. }
  266. impl From<Vec<HatchSetting>> for LegacyHatchSetting {
  267. fn from(value: Vec<HatchSetting>) -> Self {
  268. LegacyHatchSetting {
  269. mark_contour: false.into(),
  270. hatch_0_enable: value.get(0).map(|x| x.enabled).unwrap_or_default(),
  271. hatch_0_pen: value.get(0).map(|x| x.pen).unwrap_or_default(),
  272. hatch_0_flags: value.get(0).map(|x| x.flags).unwrap_or_default(),
  273. hatch_0_edge_offset: value.get(0).map(|x| x.edge_offset).unwrap_or_default(),
  274. hatch_0_line_spacing: value.get(0).map(|x| x.line_spacing).unwrap_or_default(),
  275. hatch_0_start_offset: value.get(0).map(|x| x.start_offset).unwrap_or_default(),
  276. hatch_0_end_offset: value.get(0).map(|x| x.end_offset).unwrap_or_default(),
  277. hatch_0_angle: value.get(0).map(|x| x.angle).unwrap_or_default(),
  278. hatch_1_enable: value.get(1).map(|x| x.enabled).unwrap_or_default(),
  279. hatch_1_pen: value.get(1).map(|x| x.pen).unwrap_or_default(),
  280. hatch_1_flags: value.get(1).map(|x| x.flags).unwrap_or_default(),
  281. hatch_1_edge_offset: value.get(1).map(|x| x.edge_offset).unwrap_or_default(),
  282. hatch_1_line_spacing: value.get(1).map(|x| x.line_spacing).unwrap_or_default(),
  283. hatch_1_start_offset: value.get(1).map(|x| x.start_offset).unwrap_or_default(),
  284. hatch_1_end_offset: value.get(1).map(|x| x.end_offset).unwrap_or_default(),
  285. hatch_1_angle: value.get(1).map(|x| x.angle).unwrap_or_default(),
  286. any_hatch_enabled: value.iter().any(|x| x.enabled.into()).into(),
  287. hatch_0_rotate_angle: value.get(0).map(|x| x.rotate_angle).unwrap_or_default(),
  288. hatch_1_rotate_angle: value.get(1).map(|x| x.rotate_angle).unwrap_or_default(),
  289. hatch_2_enable: value.get(2).map(|x| x.enabled).unwrap_or_default(),
  290. hatch_2_pen: value.get(2).map(|x| x.pen).unwrap_or_default(),
  291. hatch_2_flags: value.get(2).map(|x| x.flags).unwrap_or_default(),
  292. hatch_2_edge_offset: value.get(2).map(|x| x.edge_offset).unwrap_or_default(),
  293. hatch_2_line_spacing: value.get(2).map(|x| x.line_spacing).unwrap_or_default(),
  294. hatch_2_start_offset: value.get(2).map(|x| x.start_offset).unwrap_or_default(),
  295. hatch_2_end_offset: value.get(2).map(|x| x.end_offset).unwrap_or_default(),
  296. hatch_2_angle: value.get(2).map(|x| x.angle).unwrap_or_default(),
  297. hatch_2_rotate_angle: value.get(2).map(|x| x.rotate_angle).unwrap_or_default(),
  298. hatch_0_line_reduction: value.get(0).map(|x| x.line_reduction).unwrap_or_default(),
  299. hatch_1_line_reduction: value.get(1).map(|x| x.line_reduction).unwrap_or_default(),
  300. hatch_2_line_reduction: value.get(2).map(|x| x.line_reduction).unwrap_or_default(),
  301. hatch_0_loop_count: value.get(0).map(|x| x.loop_count).unwrap_or_default(),
  302. hatch_1_loop_count: value.get(1).map(|x| x.loop_count).unwrap_or_default(),
  303. hatch_2_loop_count: value.get(2).map(|x| x.loop_count).unwrap_or_default(),
  304. hatch_0_loop_distance: value.get(0).map(|x| x.loop_distance).unwrap_or_default(),
  305. hatch_1_loop_distance: value.get(1).map(|x| x.loop_distance).unwrap_or_default(),
  306. hatch_2_loop_distance: value.get(2).map(|x| x.loop_distance).unwrap_or_default(),
  307. _unknown_1: [
  308. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  309. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  310. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  311. ]
  312. .into(),
  313. contour_priority: false.into(),
  314. hatch_0_count: value.get(0).map(|x| x.count).unwrap_or_default(),
  315. hatch_1_count: value.get(1).map(|x| x.count).unwrap_or_default(),
  316. hatch_2_count: value.get(2).map(|x| x.count).unwrap_or_default(),
  317. }
  318. }
  319. }
  320. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  321. #[diff(attr(
  322. #[derive(Debug, PartialEq)]
  323. ))]
  324. pub struct HatchLine {
  325. pub lines: ArrayOf<Lines>,
  326. }
  327. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  328. #[diff(attr(
  329. #[derive(Debug, PartialEq)]
  330. ))]
  331. #[brw(magic(16_u32))] // ObjectType::HatchLine
  332. pub struct HatchLines {
  333. pub core: ObjectCore, // HatchType::HatchLine
  334. pub hatch_line: ArrayOf<HatchLine>,
  335. }
  336. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  337. #[diff(attr(
  338. #[derive(Debug, PartialEq)]
  339. ))]
  340. pub struct Hatches {
  341. pub core: ObjectCore, // HatchType::HatchLine
  342. pub hatch_lines: ArrayOf<HatchLines>,
  343. }
  344. impl Hatches {
  345. pub fn set_pen(&mut self, pen: u32) {
  346. // Ignore self.core.pen
  347. self.hatch_lines.iter_mut().for_each(|x| {
  348. // Ignore HatchLines.core.pen
  349. x.hatch_line.iter_mut().for_each(|x| {
  350. x.lines.iter_mut().for_each(|x| {
  351. x.core.pen = pen.into();
  352. })
  353. })
  354. });
  355. }
  356. }
  357. #[binrw]
  358. #[derive(Clone, Debug, Diff, PartialEq)]
  359. #[diff(attr(
  360. #[derive(Debug, PartialEq)]
  361. ))]
  362. pub struct Hatch {
  363. pub core: ObjectCore,
  364. pub outline: ArrayOf<Object>,
  365. pub legacy_setting: LegacyHatchSetting,
  366. #[br(temp)]
  367. #[bw(try_calc(u32::try_from(hatch_settings.len()).unwrap().try_into()))]
  368. pub num_settings: U32,
  369. pub _unknown_1: Field,
  370. #[br(count = num_settings.value)]
  371. pub hatch_settings: Vec<HatchSetting>,
  372. #[brw(if(legacy_setting.any_hatch_enabled.value == 1))]
  373. pub hatches: Option<Hatches>,
  374. }
  375. impl Display for Hatch {
  376. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  377. write!(f, "{}", self.core)?;
  378. for object in self.outline.iter() {
  379. write!(f, "\nOutline: {}", object)?;
  380. }
  381. write!(
  382. f,
  383. "\nMark Contour: {}, Contour Priority: {}",
  384. *self.legacy_setting.mark_contour != 0,
  385. *self.legacy_setting.contour_priority != 0
  386. )?;
  387. for (index, setting) in self.hatch_settings.iter().enumerate() {
  388. if setting.enabled.into() {
  389. write!(
  390. f,
  391. "\nHatch #{}: All Calc: {}, Follow Edge Once: {}, Cross Hatch: {}, Pattern: {}, Angle: {:.2}, Pen: {}, Count: {}, Line Space: {:.2}, Avg Distribte Line: {}",
  392. index,
  393. setting.flags.all_calc() != 0,
  394. setting.flags.follow_edge_once() != 0,
  395. setting.flags.cross_hatch() != 0,
  396. HatchPattern::from(*setting.flags),
  397. *setting.angle,
  398. *setting.pen,
  399. *setting.count,
  400. *setting.line_spacing,
  401. setting.flags.average_distribute_line() != 0,
  402. )?;
  403. write!(
  404. f,
  405. "\n Edge Offset: {:.2}, Start Offset: {:.2}, End Offset: {:.2}, Line Reduction: {:.2}, Loop Count: {}, Loop Distance: {:.2}, Auto Rotate: {}, Auto Rotate Angle: {:.2}",
  406. *setting.edge_offset,
  407. *setting.start_offset,
  408. *setting.end_offset,
  409. *setting.line_reduction,
  410. *setting.loop_count,
  411. *setting.loop_distance,
  412. setting.flags.auto_rotate_hatch_angle() != 0,
  413. *setting.rotate_angle,
  414. )?;
  415. }
  416. }
  417. Ok(())
  418. }
  419. }
  420. impl Translate for Hatch {
  421. fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
  422. self.outline
  423. .iter_mut()
  424. .for_each(|x| x.move_absolute(origin, z));
  425. origin.map(|origin| {
  426. let delta: Coordinate = origin - *self.core.origin;
  427. self.hatches.iter_mut().for_each(|x| {
  428. // Ignore Hatches.core.origin
  429. x.hatch_lines.iter_mut().for_each(|x| {
  430. // Ignore HatchLines.core.origin
  431. x.hatch_line.iter_mut().for_each(|x| {
  432. x.lines
  433. .iter_mut()
  434. .for_each(|x| x.move_relative(Some(delta), None));
  435. })
  436. })
  437. });
  438. });
  439. self.core.move_absolute(origin, z);
  440. }
  441. fn move_relative(&mut self, delta: Option<Coordinate>, z: Option<f64>) {
  442. self.outline
  443. .iter_mut()
  444. .for_each(|x| x.move_relative(delta, z));
  445. delta.map(|delta| {
  446. self.hatches.iter_mut().for_each(|x| {
  447. // Ignore Hatches.core.origin
  448. x.hatch_lines.iter_mut().for_each(|x| {
  449. // Ignore HatchLines.core.origin
  450. x.hatch_line.iter_mut().for_each(|x| {
  451. x.lines
  452. .iter_mut()
  453. .for_each(|x| x.move_relative(Some(delta), None));
  454. })
  455. })
  456. });
  457. });
  458. self.core.move_relative(delta, z);
  459. }
  460. }