Browse Source

Working hatch implementation

Kevin Lee 1 year ago
parent
commit
6cabd2fdf3

+ 2 - 0
samples/config.yml

@@ -48,3 +48,5 @@ Ops:
     Spacing: 0.5
     Z: 0.0
     StartingPen: 0
+    Hatch:
+      LineSpacing: 0.1

+ 2 - 2
src/config/mod.rs

@@ -2,7 +2,7 @@ use ezcad::{array_of::ArrayOf, layer::Layer, pen::Pen};
 use serde::{Deserialize, Serialize};
 
 use self::{
-    object::{DeleteObjects, ExportObject, ImportObject, RectangleArray},
+    object::{DeleteObjects, ExportObject, HatchArray, ImportObject},
     pen::{ClonePen, ImportExportPen, PatchPen, PatternPen},
 };
 
@@ -19,7 +19,7 @@ pub enum Operation {
     ExportObject(ExportObject),
     ImportObject(ImportObject),
     DeleteObjects(DeleteObjects),
-    RectangleArray(RectangleArray),
+    RectangleArray(HatchArray),
 }
 
 pub trait Operations {

+ 114 - 8
src/config/object.rs

@@ -8,9 +8,13 @@ use binrw::{BinRead, BinWriterExt};
 use ezcad::{
     array_of::ArrayOf,
     layer::Layer,
-    objects::{rectangle::Rectangle, Object},
+    objects::{
+        hatch::{Hatch, HatchFlag, HatchPattern, HatchSetting, Hatches},
+        rectangle::Rectangle,
+        Object, ObjectCore,
+    },
     pen::Pen,
-    types::Point,
+    types::{ObjectType, Point},
 };
 use log::debug;
 use rand::{seq::SliceRandom, thread_rng};
@@ -120,9 +124,88 @@ impl DeleteObjects {
     }
 }
 
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+pub struct HatchConfig {
+    line_spacing: f64,
+
+    count: Option<u32>,
+    edge_offset: Option<f64>,
+    start_offset: Option<f64>,
+    end_offset: Option<f64>,
+    angle: Option<f64>,
+    rotate_angle: Option<f64>,
+    line_reduction: Option<f64>,
+    loop_distance: Option<f64>,
+    loop_count: Option<u32>,
+
+    pattern: Option<HatchPattern>,
+    follow_edge_once: Option<bool>,
+    cross_hatch: Option<bool>,
+}
+
+impl From<HatchConfig> for HatchSetting {
+    fn from(value: HatchConfig) -> Self {
+        let mut ret = Self::default();
+
+        *ret.line_spacing = value.line_spacing;
+
+        if let Some(count) = value.count {
+            *ret.count = count.into();
+        }
+        if let Some(edge_offset) = value.edge_offset {
+            *ret.edge_offset = edge_offset.into();
+        }
+        if let Some(start_offset) = value.start_offset {
+            *ret.start_offset = start_offset.into();
+        }
+        if let Some(end_offset) = value.end_offset {
+            *ret.end_offset = end_offset.into();
+        }
+        if let Some(angle) = value.angle {
+            *ret.angle = angle.into();
+        }
+        if let Some(rotate_angle) = value.rotate_angle {
+            *ret.rotate_angle = rotate_angle.into();
+        }
+        if let Some(line_reduction) = value.line_reduction {
+            *ret.line_reduction = line_reduction.into();
+        }
+        if let Some(line_reduction) = value.line_reduction {
+            *ret.line_reduction = line_reduction.into();
+        }
+        if let Some(loop_distance) = value.loop_distance {
+            *ret.loop_distance = loop_distance.into();
+        }
+        if let Some(loop_count) = value.loop_count {
+            *ret.loop_count = loop_count.into();
+        }
+
+        let mut flags: HatchFlag = match value.pattern {
+            Some(pattern) => pattern.into(),
+            None => HatchPattern::Directional.into(),
+        };
+
+        if let Some(follow_edge) = value.follow_edge_once {
+            flags.set_follow_edge_once(follow_edge.into());
+        }
+        if let Some(cross_hatch) = value.cross_hatch {
+            flags.set_cross_hatch(cross_hatch.into());
+        }
+        if value.start_offset.is_none() && value.end_offset.is_none() {
+            flags.set_average_distribute_line(1);
+        }
+
+        *ret.flags = flags;
+        *ret.enabled = true.into();
+
+        ret
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "PascalCase")]
-pub struct RectangleArray {
+pub struct HatchArray {
     layer: usize,
     width: f64,
     height: f64,
@@ -131,9 +214,10 @@ pub struct RectangleArray {
     spacing: f64,
     z: f64,
     starting_pen: usize,
+    hatch: HatchConfig,
 }
 
-impl RectangleArray {
+impl HatchArray {
     pub fn generate(&self, pens: &Vec<Pen>, layers: &mut ArrayOf<Layer>) {
         debug!(
             "Generating {} x {} array of {} x {} rectangles with spacing of {} starting from pen #{} on layer #{}",
@@ -175,7 +259,10 @@ impl RectangleArray {
         seq.shuffle(&mut thread_rng());
 
         // Generate objects and append to layer
-        for (pen_incr, obj_idx) in seq.into_iter().enumerate() {
+        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);
 
@@ -188,15 +275,34 @@ impl RectangleArray {
                 y: origin.y + self.height / 2.0,
             };
             *rect.core.origin = origin;
-            *rect.core.pen = (self.starting_pen + pen_incr).try_into().unwrap();
+            *rect.core.pen = pen;
             *rect.core.z = self.z;
 
             debug!(
-                "Adding rectangle with pen #{} at {} (from {} to {})",
+                "Adding hatched rectangle with pen #{} at {} (from {} to {})",
                 *rect.core.pen, *rect.core.origin, *rect.corner_a, *rect.corner_b
             );
 
-            layer.objects.push(Object::Rectangle(rect));
+            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));
         }
     }
 }

+ 1 - 4
src/ezcad/objects/circle.rs

@@ -42,10 +42,7 @@ impl Debug for Circle {
 impl Default for Circle {
     fn default() -> Self {
         Self {
-            core: ObjectCore {
-                obj_type: ObjectType::Circle.into(),
-                ..Default::default()
-            },
+            core: ObjectCore::default(ObjectType::Circle),
             origin: Point { x: 0.0, y: 0.0 }.into(),
             radius: 0.0.into(),
             start_angle: 0.0.into(),

+ 1 - 4
src/ezcad/objects/ellipse.rs

@@ -46,10 +46,7 @@ impl Debug for Ellipse {
 impl Default for Ellipse {
     fn default() -> Self {
         Self {
-            core: ObjectCore {
-                obj_type: ObjectType::Ellipse.into(),
-                ..Default::default()
-            },
+            core: ObjectCore::default(ObjectType::Ellipse),
             clockwise: 0.into(),
             corner_a: Point { x: 0.0, y: 0.0 }.into(),
             corner_b: Point { x: 0.0, y: 0.0 }.into(),

+ 57 - 19
src/ezcad/objects/hatch.rs

@@ -1,17 +1,17 @@
 use std::fmt::Debug;
 
+use crate::{
+    array_of::ArrayOf,
+    field_of::FieldOf,
+    types::{Field, F64, U32},
+};
 use binrw::{binrw, BinRead, BinWrite};
 use diff::Diff;
 use modular_bitfield::{
     bitfield,
     specifiers::{B1, B18},
 };
-
-use crate::{
-    array_of::ArrayOf,
-    field_of::FieldOf,
-    types::{Field, F64, U32},
-};
+use serde::{Deserialize, Serialize};
 
 use super::{line::Lines, Object, ObjectCore};
 
@@ -25,24 +25,62 @@ use super::{line::Lines, Object, ObjectCore};
 pub struct HatchFlag {
     pub all_calc: B1,
     pub follow_edge_once: B1,
-    pub continuous_pattern: B1, // Hatch type 4 when combined with bidirectional_pattern
-    pub bidirectional_pattern: B1, // Hatch type 2
-    pub ring_pattern: B1,       // Hatch type 3
+    pub continuous_pattern: B1, // Hatch type 3 when combined with bidirectional_pattern
+    pub bidirectional_pattern: B1, // Hatch type 1
+    pub ring_pattern: B1,       // Hatch type 2
     #[skip]
     __: B1,
     pub auto_rotate_hatch_angle: B1,
     pub average_distribute_line: B1,
     #[skip]
     __: B1,
-    pub gong_pattern: B1, // Hatch type 5 when combined with bidirectional_pattern
+    pub gong_pattern: B1, // Hatch type 4 when combined with bidirectional_pattern
     pub cross_hatch: B1,
-    pub background_pattern: B1, // Hatch type 6, must set all_calc as well
-    pub fill_pattern: B1, // Hatch type 7 when combined with continuous_pattern and bidirectional_pattern
-    pub zigzag_pattern: B1, // Hatch type 8
+    pub background_pattern: B1, // Hatch type 5, must set all_calc as well
+    pub fill_pattern: B1, // Hatch type 6 when combined with continuous_pattern and bidirectional_pattern
+    pub zigzag_pattern: B1, // Hatch type 7
     #[skip]
     __: B18,
 }
 
+#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
+pub enum HatchPattern {
+    Directional,
+    Bidirectional,
+    Ring,
+    Continuous,
+    Gong,
+    Background,
+    Fill,
+    Zigzag,
+}
+
+impl From<HatchPattern> for HatchFlag {
+    fn from(value: HatchPattern) -> Self {
+        let mut ret = Self::new();
+        match value {
+            HatchPattern::Directional => (),
+            HatchPattern::Bidirectional => ret.set_bidirectional_pattern(1),
+            HatchPattern::Ring => ret.set_ring_pattern(1),
+            HatchPattern::Continuous => {
+                ret.set_bidirectional_pattern(1);
+                ret.set_continuous_pattern(1);
+            }
+            HatchPattern::Gong => {
+                ret.set_bidirectional_pattern(1);
+                ret.set_gong_pattern(1);
+            }
+            HatchPattern::Background => {
+                ret.set_background_pattern(1);
+                ret.set_all_calc(1);
+            }
+            HatchPattern::Fill => ret.set_fill_pattern(1),
+            HatchPattern::Zigzag => ret.set_zigzag_pattern(1),
+        }
+        ret
+    }
+}
+
 #[cfg_attr(feature = "default-debug", derive(Debug))]
 #[derive(BinRead, BinWrite, Diff, PartialEq)]
 #[diff(attr(
@@ -147,7 +185,7 @@ impl Debug for LegacyHatchSetting {
 }
 
 #[cfg_attr(feature = "default-debug", derive(Debug))]
-#[derive(BinRead, BinWrite, Diff, PartialEq)]
+#[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]
@@ -199,13 +237,13 @@ impl Default for HatchSetting {
             pen: 0.into(),
             flags: HatchFlag::new().into(),
             edge_offset: 0.0.into(),
-            line_spacing: 0.0.into(),
+            line_spacing: 1.0.into(),
             start_offset: 0.0.into(),
             end_offset: 0.0.into(),
             angle: 0.0.into(),
-            rotate_angle: 0.0.into(),
+            rotate_angle: 10.0.into(),
             line_reduction: 0.0.into(),
-            loop_distance: 0.0.into(),
+            loop_distance: 0.5.into(),
             loop_count: 0.into(),
             _unknown_1: [vec![0, 0, 0, 0, 0, 0, 0, 0].into(), vec![0, 0, 0, 0].into()].into(),
         }
@@ -281,7 +319,7 @@ pub struct HatchLine {
 ))]
 #[brw(magic(16_u32))] // ObjectType::HatchLine
 pub struct HatchLines {
-    pub core: ObjectCore,
+    pub core: ObjectCore, // HatchType::HatchLine
     pub hatch_line: ArrayOf<HatchLine>,
 }
 
@@ -290,7 +328,7 @@ pub struct HatchLines {
     #[derive(Debug, PartialEq)]
 ))]
 pub struct Hatches {
-    pub core: ObjectCore,
+    pub core: ObjectCore, // HatchType::HatchLine
     pub hatch_lines: ArrayOf<HatchLines>,
 }
 

+ 5 - 5
src/ezcad/objects/mod.rs

@@ -51,10 +51,10 @@ pub struct ObjectCore {
     pub flags: FieldOf<ObjectFlags>,
     pub name: WString,
     pub count: U32,
-    _unknown_1: Field,
+    pub _unknown_1: Field,
     pub io_control_enable_mask: U16,
     pub io_control_disable_mask: U16,
-    _unknown_2: [Field; 5],
+    pub _unknown_2: [Field; 5],
     pub origin: FieldOf<Point>,
     pub z: F64,
     pub a: F64,
@@ -81,11 +81,11 @@ impl Debug for ObjectCore {
     }
 }
 
-impl Default for ObjectCore {
-    fn default() -> Self {
+impl ObjectCore {
+    pub fn default(obj_type: ObjectType) -> Self {
         Self {
             pen: 0.into(),
-            obj_type: ObjectType::Unknown.into(),
+            obj_type: obj_type.into(),
             flags: ObjectFlags::new()
                 .with_disabled(0)
                 .with_aspect_ratio_unlocked(0)

+ 1 - 4
src/ezcad/objects/polygon.rs

@@ -50,10 +50,7 @@ impl Debug for Polygon {
 impl Default for Polygon {
     fn default() -> Self {
         Self {
-            core: ObjectCore {
-                obj_type: ObjectType::Polygon.into(),
-                ..Default::default()
-            },
+            core: ObjectCore::default(ObjectType::Polygon),
             invert_shape: 1.into(),
             corner_a: Point { x: 0.0, y: 0.0 }.into(),
             corner_b: Point { x: 0.0, y: 0.0 }.into(),

+ 1 - 4
src/ezcad/objects/rectangle.rs

@@ -46,10 +46,7 @@ impl Debug for Rectangle {
 impl Default for Rectangle {
     fn default() -> Self {
         Self {
-            core: ObjectCore {
-                obj_type: ObjectType::Rectangle.into(),
-                ..Default::default()
-            },
+            core: ObjectCore::default(ObjectType::Rectangle),
             corner_a: Point { x: 0.0, y: 0.0 }.into(),
             corner_b: Point { x: 0.0, y: 0.0 }.into(),
             round_bottom_left: 0.0.into(),

+ 1 - 1
src/ezcad/types.rs

@@ -120,7 +120,7 @@ impl Rgba {
     }
 }
 
-#[derive(Clone, Debug, Default, Diff, PartialEq, BinRead, BinWrite)]
+#[derive(Copy, Clone, Debug, Default, Diff, PartialEq, BinRead, BinWrite)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]

+ 0 - 12
src/main.rs

@@ -140,18 +140,6 @@ fn main() {
         trace!("Config processing time: {:?}", time.elapsed());
     }
 
-    let first_obj = &mut file.layers_offset[0].objects[0];
-    match first_obj {
-        ezcad::objects::Object::Hatch(h) => {
-            *h.hatch_settings[0].pen = 3;
-            *h.hatch_settings[0].line_spacing = 0.5;
-            if let Some(hatches) = &mut h.hatches {
-                hatches.hatch_lines.clear()
-            }
-        }
-        _ => (),
-    }
-
     // Process output
     if let Some(output) = cli.output {
         info!("Writing output file '{}'", output.to_string_lossy());