hatch.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. use std::fmt::Debug;
  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)]
  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. #[cfg_attr(feature = "default-debug", derive(Debug))]
  79. #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
  80. #[diff(attr(
  81. #[derive(Debug, PartialEq)]
  82. ))]
  83. // #[brw(magic(46u32))] // Number of fields in this struct
  84. #[brw(magic(47u32))] // Number of fields in this struct
  85. pub struct LegacyHatchSetting {
  86. pub mark_contour: U32,
  87. pub hatch_0_enable: U32,
  88. pub hatch_0_pen: U32,
  89. pub hatch_0_flags: FieldOf<HatchFlag>,
  90. pub hatch_0_edge_offset: F64,
  91. pub hatch_0_line_spacing: F64,
  92. pub hatch_0_start_offset: F64,
  93. pub hatch_0_end_offset: F64,
  94. pub hatch_0_angle: F64,
  95. pub hatch_1_enable: U32,
  96. pub hatch_1_pen: U32,
  97. pub hatch_1_flags: FieldOf<HatchFlag>,
  98. pub hatch_1_edge_offset: F64,
  99. pub hatch_1_line_spacing: F64,
  100. pub hatch_1_start_offset: F64,
  101. pub hatch_1_end_offset: F64,
  102. pub hatch_1_angle: F64,
  103. pub any_hatch_enabled: U32,
  104. pub hatch_0_rotate_angle: F64,
  105. pub hatch_1_rotate_angle: F64,
  106. pub hatch_2_enable: U32,
  107. pub hatch_2_pen: U32,
  108. pub hatch_2_flags: FieldOf<HatchFlag>,
  109. pub hatch_2_edge_offset: F64,
  110. pub hatch_2_line_spacing: F64,
  111. pub hatch_2_start_offset: F64,
  112. pub hatch_2_end_offset: F64,
  113. pub hatch_2_angle: F64,
  114. pub hatch_2_rotate_angle: F64,
  115. pub hatch_0_line_reduction: F64,
  116. pub hatch_1_line_reduction: F64,
  117. pub hatch_2_line_reduction: F64,
  118. pub hatch_0_loop_count: U32,
  119. pub hatch_1_loop_count: U32,
  120. pub hatch_2_loop_count: U32,
  121. pub hatch_0_loop_distance: F64,
  122. pub hatch_1_loop_distance: F64,
  123. pub hatch_2_loop_distance: F64,
  124. pub _unknown_1: [Field; 3],
  125. pub contour_priority: U32,
  126. pub hatch_0_count: U32,
  127. pub hatch_1_count: U32,
  128. pub hatch_2_count: U32,
  129. }
  130. // Custom Debug implementation to only print known fields
  131. #[cfg(not(feature = "default-debug"))]
  132. impl Debug for LegacyHatchSetting {
  133. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  134. f.debug_struct("LegacyHatchSetting")
  135. .field("mark_contour", &self.mark_contour)
  136. .field("hatch_0_enable", &self.hatch_0_enable)
  137. .field("hatch_0_pen", &self.hatch_0_pen)
  138. .field("hatch_0_flags", &self.hatch_0_flags)
  139. .field("hatch_0_edge_offset", &self.hatch_0_edge_offset)
  140. .field("hatch_0_line_spacing", &self.hatch_0_line_spacing)
  141. .field("hatch_0_start_offset", &self.hatch_0_start_offset)
  142. .field("hatch_0_end_offset", &self.hatch_0_end_offset)
  143. .field("hatch_0_angle", &self.hatch_0_angle)
  144. .field("hatch_1_enable", &self.hatch_1_enable)
  145. .field("hatch_1_pen", &self.hatch_1_pen)
  146. .field("hatch_1_flags", &self.hatch_1_flags)
  147. .field("hatch_1_edge_offset", &self.hatch_1_edge_offset)
  148. .field("hatch_1_line_spacing", &self.hatch_1_line_spacing)
  149. .field("hatch_1_start_offset", &self.hatch_1_start_offset)
  150. .field("hatch_1_end_offset", &self.hatch_1_end_offset)
  151. .field("hatch_1_angle", &self.hatch_1_angle)
  152. .field("any_hatch_enabled", &self.any_hatch_enabled)
  153. .field("hatch_0_auto_rotate_angle", &self.hatch_0_rotate_angle)
  154. .field("hatch_1_auto_rotate_angle", &self.hatch_1_rotate_angle)
  155. .field("hatch_2_enable", &self.hatch_2_enable)
  156. .field("hatch_2_pen", &self.hatch_2_pen)
  157. .field("hatch_2_flags", &self.hatch_2_flags)
  158. .field("hatch_2_edge_offset", &self.hatch_2_edge_offset)
  159. .field("hatch_2_line_spacing", &self.hatch_2_line_spacing)
  160. .field("hatch_2_start_offset", &self.hatch_2_start_offset)
  161. .field("hatch_2_end_offset", &self.hatch_2_end_offset)
  162. .field("hatch_2_angle", &self.hatch_2_angle)
  163. .field("hatch_2_auto_rotate_angle", &self.hatch_2_rotate_angle)
  164. .field("hatch_0_line_reduction", &self.hatch_0_line_reduction)
  165. .field("hatch_1_line_reduction", &self.hatch_1_line_reduction)
  166. .field("hatch_2_line_reduction", &self.hatch_2_line_reduction)
  167. .field("hatch_0_loop_count", &self.hatch_0_loop_count)
  168. .field("hatch_1_loop_count", &self.hatch_1_loop_count)
  169. .field("hatch_2_loop_count", &self.hatch_2_loop_count)
  170. .field("hatch_0_loop_distance", &self.hatch_0_loop_distance)
  171. .field("hatch_1_loop_distance", &self.hatch_1_loop_distance)
  172. .field("hatch_2_loop_distance", &self.hatch_2_loop_distance)
  173. .field("contour_priority", &self.contour_priority)
  174. .field("hatch_0_count", &self.hatch_0_count)
  175. .field("hatch_1_count", &self.hatch_1_count)
  176. .field("hatch_2_count", &self.hatch_2_count)
  177. .finish()
  178. }
  179. }
  180. #[cfg_attr(feature = "default-debug", derive(Debug))]
  181. #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
  182. #[diff(attr(
  183. #[derive(Debug, PartialEq)]
  184. ))]
  185. #[brw(magic(15u32))] // Number of fields in this struct
  186. pub struct HatchSetting {
  187. pub count: U32,
  188. pub enabled: U32,
  189. pub pen: U32,
  190. pub flags: FieldOf<HatchFlag>,
  191. pub edge_offset: F64,
  192. pub line_spacing: F64,
  193. pub start_offset: F64,
  194. pub end_offset: F64,
  195. pub angle: F64,
  196. pub rotate_angle: F64,
  197. pub line_reduction: F64,
  198. pub loop_distance: F64,
  199. pub loop_count: U32,
  200. pub _unknown_1: [Field; 2],
  201. }
  202. // Custom Debug implementation to only print known fields
  203. #[cfg(not(feature = "default-debug"))]
  204. impl Debug for HatchSetting {
  205. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  206. f.debug_struct("HatchSettings")
  207. .field("count", &self.count)
  208. .field("enabled", &self.enabled)
  209. .field("pen", &self.pen)
  210. .field("flags", &self.flags)
  211. .field("edge_offset", &self.edge_offset)
  212. .field("line_spacing", &self.line_spacing)
  213. .field("start_offset", &self.start_offset)
  214. .field("end_offset", &self.end_offset)
  215. .field("angle", &self.angle)
  216. .field("auto_rotate_angle", &self.rotate_angle)
  217. .field("line_reduction", &self.line_reduction)
  218. .field("loop_distance", &self.loop_distance)
  219. .field("loop_count", &self.loop_count)
  220. .finish()
  221. }
  222. }
  223. impl Default for HatchSetting {
  224. fn default() -> Self {
  225. Self {
  226. count: 1.into(),
  227. enabled: false.into(),
  228. pen: 0.into(),
  229. flags: HatchFlag::new().into(),
  230. edge_offset: 0.0.into(),
  231. line_spacing: 1.0.into(),
  232. start_offset: 0.0.into(),
  233. end_offset: 0.0.into(),
  234. angle: 0.0.into(),
  235. rotate_angle: 10.0.into(),
  236. line_reduction: 0.0.into(),
  237. loop_distance: 0.5.into(),
  238. loop_count: 0.into(),
  239. _unknown_1: [vec![0, 0, 0, 0, 0, 0, 0, 0].into(), vec![0, 0, 0, 0].into()].into(),
  240. }
  241. }
  242. }
  243. impl From<Vec<HatchSetting>> for LegacyHatchSetting {
  244. fn from(value: Vec<HatchSetting>) -> Self {
  245. LegacyHatchSetting {
  246. mark_contour: false.into(),
  247. hatch_0_enable: value.get(0).map(|x| x.enabled).unwrap_or_default(),
  248. hatch_0_pen: value.get(0).map(|x| x.pen).unwrap_or_default(),
  249. hatch_0_flags: value.get(0).map(|x| x.flags).unwrap_or_default(),
  250. hatch_0_edge_offset: value.get(0).map(|x| x.edge_offset).unwrap_or_default(),
  251. hatch_0_line_spacing: value.get(0).map(|x| x.line_spacing).unwrap_or_default(),
  252. hatch_0_start_offset: value.get(0).map(|x| x.start_offset).unwrap_or_default(),
  253. hatch_0_end_offset: value.get(0).map(|x| x.end_offset).unwrap_or_default(),
  254. hatch_0_angle: value.get(0).map(|x| x.angle).unwrap_or_default(),
  255. hatch_1_enable: value.get(1).map(|x| x.enabled).unwrap_or_default(),
  256. hatch_1_pen: value.get(1).map(|x| x.pen).unwrap_or_default(),
  257. hatch_1_flags: value.get(1).map(|x| x.flags).unwrap_or_default(),
  258. hatch_1_edge_offset: value.get(1).map(|x| x.edge_offset).unwrap_or_default(),
  259. hatch_1_line_spacing: value.get(1).map(|x| x.line_spacing).unwrap_or_default(),
  260. hatch_1_start_offset: value.get(1).map(|x| x.start_offset).unwrap_or_default(),
  261. hatch_1_end_offset: value.get(1).map(|x| x.end_offset).unwrap_or_default(),
  262. hatch_1_angle: value.get(1).map(|x| x.angle).unwrap_or_default(),
  263. any_hatch_enabled: value.iter().any(|x| x.enabled.into()).into(),
  264. hatch_0_rotate_angle: value.get(0).map(|x| x.rotate_angle).unwrap_or_default(),
  265. hatch_1_rotate_angle: value.get(1).map(|x| x.rotate_angle).unwrap_or_default(),
  266. hatch_2_enable: value.get(2).map(|x| x.enabled).unwrap_or_default(),
  267. hatch_2_pen: value.get(2).map(|x| x.pen).unwrap_or_default(),
  268. hatch_2_flags: value.get(2).map(|x| x.flags).unwrap_or_default(),
  269. hatch_2_edge_offset: value.get(2).map(|x| x.edge_offset).unwrap_or_default(),
  270. hatch_2_line_spacing: value.get(2).map(|x| x.line_spacing).unwrap_or_default(),
  271. hatch_2_start_offset: value.get(2).map(|x| x.start_offset).unwrap_or_default(),
  272. hatch_2_end_offset: value.get(2).map(|x| x.end_offset).unwrap_or_default(),
  273. hatch_2_angle: value.get(2).map(|x| x.angle).unwrap_or_default(),
  274. hatch_2_rotate_angle: value.get(2).map(|x| x.rotate_angle).unwrap_or_default(),
  275. hatch_0_line_reduction: value.get(0).map(|x| x.line_reduction).unwrap_or_default(),
  276. hatch_1_line_reduction: value.get(1).map(|x| x.line_reduction).unwrap_or_default(),
  277. hatch_2_line_reduction: value.get(2).map(|x| x.line_reduction).unwrap_or_default(),
  278. hatch_0_loop_count: value.get(0).map(|x| x.loop_count).unwrap_or_default(),
  279. hatch_1_loop_count: value.get(1).map(|x| x.loop_count).unwrap_or_default(),
  280. hatch_2_loop_count: value.get(2).map(|x| x.loop_count).unwrap_or_default(),
  281. hatch_0_loop_distance: value.get(0).map(|x| x.loop_distance).unwrap_or_default(),
  282. hatch_1_loop_distance: value.get(1).map(|x| x.loop_distance).unwrap_or_default(),
  283. hatch_2_loop_distance: value.get(2).map(|x| x.loop_distance).unwrap_or_default(),
  284. _unknown_1: [
  285. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  286. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  287. vec![0, 0, 0, 0, 0, 0, 0, 0].into(),
  288. ]
  289. .into(),
  290. contour_priority: false.into(),
  291. hatch_0_count: value.get(0).map(|x| x.count).unwrap_or_default(),
  292. hatch_1_count: value.get(1).map(|x| x.count).unwrap_or_default(),
  293. hatch_2_count: value.get(2).map(|x| x.count).unwrap_or_default(),
  294. }
  295. }
  296. }
  297. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  298. #[diff(attr(
  299. #[derive(Debug, PartialEq)]
  300. ))]
  301. pub struct HatchLine {
  302. pub lines: ArrayOf<Lines>,
  303. }
  304. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  305. #[diff(attr(
  306. #[derive(Debug, PartialEq)]
  307. ))]
  308. #[brw(magic(16_u32))] // ObjectType::HatchLine
  309. pub struct HatchLines {
  310. pub core: ObjectCore, // HatchType::HatchLine
  311. pub hatch_line: ArrayOf<HatchLine>,
  312. }
  313. #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
  314. #[diff(attr(
  315. #[derive(Debug, PartialEq)]
  316. ))]
  317. pub struct Hatches {
  318. pub core: ObjectCore, // HatchType::HatchLine
  319. pub hatch_lines: ArrayOf<HatchLines>,
  320. }
  321. impl Hatches {
  322. pub fn set_pen(&mut self, pen: u32) {
  323. // Ignore self.core.pen
  324. self.hatch_lines.iter_mut().for_each(|x| {
  325. // Ignore HatchLines.core.pen
  326. x.hatch_line.iter_mut().for_each(|x| {
  327. x.lines.iter_mut().for_each(|x| {
  328. x.core.pen = pen.into();
  329. })
  330. })
  331. });
  332. }
  333. }
  334. #[binrw]
  335. #[derive(Clone, Debug, Diff, PartialEq)]
  336. #[diff(attr(
  337. #[derive(Debug, PartialEq)]
  338. ))]
  339. pub struct Hatch {
  340. pub core: ObjectCore,
  341. pub outline: ArrayOf<Object>,
  342. pub legacy_setting: LegacyHatchSetting,
  343. #[br(temp)]
  344. #[bw(try_calc(u32::try_from(hatch_settings.len()).unwrap().try_into()))]
  345. pub num_settings: U32,
  346. pub _unknown_1: Field,
  347. #[br(count = num_settings.value)]
  348. pub hatch_settings: Vec<HatchSetting>,
  349. #[brw(if(legacy_setting.any_hatch_enabled.value == 1))]
  350. pub hatches: Option<Hatches>,
  351. }
  352. impl Translate for Hatch {
  353. fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
  354. self.outline
  355. .iter_mut()
  356. .for_each(|x| x.move_absolute(origin, z));
  357. origin.map(|origin| {
  358. let delta: Coordinate = origin - *self.core.origin;
  359. self.hatches.iter_mut().for_each(|x| {
  360. // Ignore Hatches.core.origin
  361. x.hatch_lines.iter_mut().for_each(|x| {
  362. // Ignore HatchLines.core.origin
  363. x.hatch_line.iter_mut().for_each(|x| {
  364. x.lines
  365. .iter_mut()
  366. .for_each(|x| x.move_relative(Some(delta), None));
  367. })
  368. })
  369. });
  370. });
  371. self.core.move_absolute(origin, z);
  372. }
  373. fn move_relative(&mut self, delta: Option<Coordinate>, z: Option<f64>) {
  374. self.outline
  375. .iter_mut()
  376. .for_each(|x| x.move_relative(delta, z));
  377. delta.map(|delta| {
  378. self.hatches.iter_mut().for_each(|x| {
  379. // Ignore Hatches.core.origin
  380. x.hatch_lines.iter_mut().for_each(|x| {
  381. // Ignore HatchLines.core.origin
  382. x.hatch_line.iter_mut().for_each(|x| {
  383. x.lines
  384. .iter_mut()
  385. .for_each(|x| x.move_relative(Some(delta), None));
  386. })
  387. })
  388. });
  389. });
  390. self.core.move_relative(delta, z);
  391. }
  392. }