9 Комити ff93f97c25 ... 5a27b88ba3

Аутор SHA1 Порука Датум
  Kevin Lee 5a27b88ba3 Change correction point пре 6 месеци
  Kevin Lee 63787168b7 Maybe working skew code пре 6 месеци
  Kevin Lee d8bbda90a5 Fix hatch object info пре 6 месеци
  Kevin Lee 664cacfc23 Skew support and origin refactoring пре 6 месеци
  Kevin Lee 81f7107216 Update samples пре 6 месеци
  Kevin Lee f803464ca6 Add missing display for line types пре 6 месеци
  Kevin Lee 49f8a16fa7 More display prints пре 6 месеци
  Kevin Lee bc71d4778d Display print WIP пре 6 месеци
  Kevin Lee 8af494f133 Object query WIP пре 6 месеци
55 измењених фајлова са 452 додато и 81 уклоњено
  1. BIN
      samples/Circle.mlp
  2. BIN
      samples/Circle2.mlp
  3. BIN
      samples/Circle3.mlp
  4. BIN
      samples/Circle4.mlp
  5. BIN
      samples/Circle5.mlp
  6. BIN
      samples/Curve.mlp
  7. BIN
      samples/Ellipse.mlp
  8. BIN
      samples/Ellipse2.mlp
  9. BIN
      samples/Ellipse3.mlp
  10. BIN
      samples/EllipseRotated.mlp
  11. BIN
      samples/EllipseRotated2.mlp
  12. BIN
      samples/EllipseSkewed.mlp
  13. BIN
      samples/EllipseSkewed2.mlp
  14. BIN
      samples/EllipseSkewed3.mlp
  15. BIN
      samples/EllipseSkewed4.mlp
  16. BIN
      samples/HatchCircle.mlp
  17. BIN
      samples/HatchCircle2.mlp
  18. BIN
      samples/HatchRectangle.mlp
  19. BIN
      samples/HatchRectangle2.mlp
  20. BIN
      samples/HatchRectangle3.mlp
  21. BIN
      samples/HatchRectangle4.mlp
  22. BIN
      samples/HatchRectangle5.mlp
  23. BIN
      samples/HatchRectangle6.mlp
  24. BIN
      samples/HatchRectangle7.mlp
  25. BIN
      samples/HatchRectangle8.mlp
  26. BIN
      samples/HatchRectangleDisabled.mlp
  27. BIN
      samples/HatchRectangleMulti.mlp
  28. BIN
      samples/HatchRectangleMulti2.mlp
  29. BIN
      samples/HatchRectangleMulti3.mlp
  30. BIN
      samples/HatchRectangleMultiOutline.mlp
  31. BIN
      samples/HatchRectangleMultiTest.mlp
  32. BIN
      samples/HatchRectangleSmall.mlp
  33. BIN
      samples/HatchRectangleSmall2.mlp
  34. BIN
      samples/Line.mlp
  35. BIN
      samples/Multicuve.mlp
  36. BIN
      samples/Multicuve2.mlp
  37. BIN
      samples/Multiline.mlp
  38. BIN
      samples/Point.mlp
  39. BIN
      samples/Polygon.mlp
  40. BIN
      samples/Polygon2.mlp
  41. BIN
      samples/Polygon3.mlp
  42. BIN
      samples/Polygon4.mlp
  43. BIN
      samples/Rectangle.mlp
  44. BIN
      samples/Rectangle2.mlp
  45. BIN
      samples/Rectangle3.mlp
  46. BIN
      samples/Rectangle4.mlp
  47. 16 1
      src/ezcad/objects/circle.rs
  48. 31 1
      src/ezcad/objects/ellipse.rs
  49. 72 2
      src/ezcad/objects/hatch.rs
  50. 43 1
      src/ezcad/objects/line.rs
  51. 154 8
      src/ezcad/objects/mod.rs
  52. 92 63
      src/ezcad/objects/polygon.rs
  53. 23 1
      src/ezcad/objects/rectangle.rs
  54. 16 0
      src/ezcad/types.rs
  55. 5 4
      src/main.rs

BIN
samples/Circle.mlp


BIN
samples/Circle2.mlp


BIN
samples/Circle3.mlp


BIN
samples/Circle4.mlp


BIN
samples/Circle5.mlp


BIN
samples/Curve.mlp


BIN
samples/Ellipse.mlp


BIN
samples/Ellipse2.mlp


BIN
samples/Ellipse3.mlp


BIN
samples/EllipseRotated.mlp


BIN
samples/EllipseRotated2.mlp


BIN
samples/EllipseSkewed.mlp


BIN
samples/EllipseSkewed2.mlp


BIN
samples/EllipseSkewed3.mlp


BIN
samples/EllipseSkewed4.mlp


BIN
samples/HatchCircle.mlp


BIN
samples/HatchCircle2.mlp


BIN
samples/HatchRectangle.mlp


BIN
samples/HatchRectangle2.mlp


BIN
samples/HatchRectangle3.mlp


BIN
samples/HatchRectangle4.mlp


BIN
samples/HatchRectangle5.mlp


BIN
samples/HatchRectangle6.mlp


BIN
samples/HatchRectangle7.mlp


BIN
samples/HatchRectangle8.mlp


BIN
samples/HatchRectangleDisabled.mlp


BIN
samples/HatchRectangleMulti.mlp


BIN
samples/HatchRectangleMulti2.mlp


BIN
samples/HatchRectangleMulti3.mlp


BIN
samples/HatchRectangleMultiOutline.mlp


BIN
samples/HatchRectangleMultiTest.mlp


BIN
samples/HatchRectangleSmall.mlp


BIN
samples/HatchRectangleSmall2.mlp



BIN
samples/Multicuve.mlp


BIN
samples/Multicuve2.mlp


BIN
samples/Multiline.mlp


BIN
samples/Point.mlp


BIN
samples/Polygon.mlp


BIN
samples/Polygon2.mlp


BIN
samples/Polygon3.mlp


BIN
samples/Polygon4.mlp


BIN
samples/Rectangle.mlp


BIN
samples/Rectangle2.mlp


BIN
samples/Rectangle3.mlp


BIN
samples/Rectangle4.mlp


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

@@ -1,4 +1,4 @@
-use std::fmt::Debug;
+use std::fmt::{Debug, Display};
 
 use binrw::{BinRead, BinWrite};
 use diff::Diff;
@@ -24,6 +24,21 @@ pub struct Circle {
     pub modifier: ObjectModifier,
 }
 
+impl Display for Circle {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{}, Origin: {}, Width : {:.2}, Height: {:.2}, Start Radian: {:.2}, Clockwise: {}",
+            self.core,
+            self.modifier.correction() + *self.drawn_origin * self.modifier.scale(),
+            *self.radius * self.modifier.modifiers.x_scale * 2.0,
+            *self.radius * self.modifier.modifiers.y_scale * 2.0,
+            *self.start_angle,
+            *self.clockwise != 0,
+        )
+    }
+}
+
 impl Default for Circle {
     fn default() -> Self {
         Self {

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

@@ -1,7 +1,8 @@
-use std::fmt::Debug;
+use std::fmt::{Debug, Display};
 
 use binrw::{BinRead, BinWrite};
 use diff::Diff;
+use log::warn;
 
 use crate::{
     field_of::FieldOf,
@@ -26,6 +27,35 @@ pub struct Ellipse {
     pub open_curve: U32,
 }
 
+impl Display for Ellipse {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let (origin, width, height) = self
+            .modifier
+            .corrected(*self.drawn_corner_a, *self.drawn_corner_b);
+
+        if format!("{}", origin) != format!("{}", *self.core.origin) {
+            warn!(
+                "Origin mismatch! Core: {}, Calculated: {}",
+                *self.core.origin, origin
+            );
+        }
+
+        write!(
+            f,
+            "{}, Origin: {}, Width: {:.2}, Height: {:.2}, Start Radian: {:.2}, End Radian: {:.2}, Open Curve: {}", 
+            self.core,
+            origin,
+            width,
+            height,
+                *self.start_angle,
+                *self.end_angle,
+                *self.open_curve != 0,
+        )?;
+        write!(f, "\n{:?}", self.modifier)?;
+        Ok(())
+    }
+}
+
 impl Default for Ellipse {
     fn default() -> Self {
         Self {

+ 72 - 2
src/ezcad/objects/hatch.rs

@@ -1,4 +1,4 @@
-use std::fmt::Debug;
+use std::fmt::{Debug, Display};
 
 use crate::{
     array_of::ArrayOf,
@@ -43,7 +43,7 @@ pub struct HatchFlag {
     __: B18,
 }
 
-#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, strum::Display)]
 pub enum HatchPattern {
     Directional,
     Bidirectional,
@@ -81,6 +81,30 @@ impl From<HatchPattern> for HatchFlag {
     }
 }
 
+impl From<HatchFlag> for HatchPattern {
+    fn from(value: HatchFlag) -> Self {
+        if value.ring_pattern() != 0 {
+            HatchPattern::Ring
+        } else if value.background_pattern() != 0 {
+            HatchPattern::Background
+        } else if value.fill_pattern() != 0 {
+            HatchPattern::Fill
+        } else if value.zigzag_pattern() != 0 {
+            HatchPattern::Zigzag
+        } else if value.gong_pattern() != 0 {
+            HatchPattern::Gong
+        } else if value.bidirectional_pattern() != 0 {
+            if value.continuous_pattern() != 0 {
+                HatchPattern::Continuous
+            } else {
+                HatchPattern::Bidirectional
+            }
+        } else {
+            HatchPattern::Directional
+        }
+    }
+}
+
 #[cfg_attr(feature = "default-debug", derive(Debug))]
 #[derive(BinRead, BinWrite, Clone, Diff, PartialEq)]
 #[diff(attr(
@@ -370,6 +394,52 @@ pub struct Hatch {
     pub hatches: Option<Hatches>,
 }
 
+impl Display for Hatch {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.core)?;
+        for object in self.outline.iter() {
+            write!(f, "\nOutline: {}", object)?;
+        }
+        write!(
+            f,
+            "\nMark Contour: {}, Contour Priority: {}",
+            *self.legacy_setting.mark_contour != 0,
+            *self.legacy_setting.contour_priority != 0
+        )?;
+        for (index, setting) in self.hatch_settings.iter().enumerate() {
+            if setting.enabled.into() {
+                write!(
+                    f,
+                    "\nHatch #{}: All Calc: {}, Follow Edge Once: {}, Cross Hatch: {}, Pattern: {}, Angle: {:.2}, Pen: {}, Count: {}, Line Space: {:.2}, Avg Distribte Line: {}",
+                    index,
+                    setting.flags.all_calc() != 0,
+                    setting.flags.follow_edge_once() != 0,
+                    setting.flags.cross_hatch() != 0,
+                    HatchPattern::from(*setting.flags),
+                    *setting.angle,
+                    *setting.pen,
+                    *setting.count,
+                    *setting.line_spacing,
+                    setting.flags.average_distribute_line() != 0,
+                )?;
+                write!(
+                    f,
+                    "\n          Edge Offset: {:.2}, Start Offset: {:.2}, End Offset: {:.2}, Line Reduction: {:.2}, Loop Count: {}, Loop Distance: {:.2}, Auto Rotate: {}, Auto Rotate Angle: {:.2}",
+                    *setting.edge_offset,
+                    *setting.start_offset,
+                    *setting.end_offset,
+                    *setting.line_reduction,
+                    *setting.loop_count,
+                    *setting.loop_distance,
+                    setting.flags.auto_rotate_hatch_angle() != 0,
+                    *setting.rotate_angle,
+                )?;
+            }
+        }
+        Ok(())
+    }
+}
+
 impl Translate for Hatch {
     fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
         self.outline

+ 43 - 1
src/ezcad/objects/line.rs

@@ -1,4 +1,4 @@
-use std::fmt::Debug;
+use std::fmt::{Debug, Display};
 
 use binrw::{binrw, BinRead, BinWrite};
 use diff::Diff;
@@ -27,6 +27,38 @@ pub enum LineType {
     },
 }
 
+impl Display for LineType {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            LineType::Point { _zero, point } => {
+                write!(f, "Point: {}", point)
+            }
+            LineType::Line { _zero, points } => {
+                write!(
+                    f,
+                    "Line: {}",
+                    points
+                        .iter()
+                        .map(|x| format!("{x}"))
+                        .collect::<Vec<String>>()
+                        .join(", ")
+                )
+            }
+            LineType::Bezier { _zero, points } => {
+                write!(
+                    f,
+                    "Bezier: {}",
+                    points
+                        .iter()
+                        .map(|x| format!("{x}"))
+                        .collect::<Vec<String>>()
+                        .join(", ")
+                )
+            }
+        }
+    }
+}
+
 // Custom Debug implementation to only print known fields
 #[cfg(not(feature = "default-debug"))]
 impl Debug for LineType {
@@ -75,6 +107,16 @@ pub struct Lines {
     pub lines: Vec<LineType>,
 }
 
+impl Display for Lines {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.core)?;
+        for line in &self.lines {
+            write!(f, "\n{}", line)?;
+        }
+        Ok(())
+    }
+}
+
 impl Translate for Lines {
     fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
         origin.map(|origin| {

+ 154 - 8
src/ezcad/objects/mod.rs

@@ -1,5 +1,5 @@
 use std::{
-    fmt::Debug,
+    fmt::{Debug, Display},
     fs::File,
     io::{Cursor, Read, Write},
     path::PathBuf,
@@ -7,6 +7,8 @@ use std::{
 
 use binrw::{BinRead, BinWrite, BinWriterExt};
 use diff::Diff;
+use itertools::Itertools;
+use log::debug;
 use modular_bitfield::{
     bitfield,
     specifiers::{B1, B14},
@@ -73,6 +75,20 @@ pub struct ObjectCore {
     pub index: U32, // Increments for each hatch fill line (types 1/2) or 0xFFFF_FFFF (types 3/4/5/7/8)
 }
 
+impl Display for ObjectCore {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "Type: {}, Enabled: {}, Pen: {}, Count: {}, Z: {:.2}",
+            self.obj_type,
+            (self.flags.disabled() == 0),
+            self.pen,
+            self.count,
+            self.z
+        )
+    }
+}
+
 // Custom Debug implementation to only print known fields
 #[cfg(not(feature = "default-debug"))]
 impl Debug for ObjectCore {
@@ -135,7 +151,7 @@ impl ObjectCore {
     }
 }
 
-#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq, strum::Display)]
+#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]
 ))]
@@ -156,6 +172,21 @@ pub enum Object {
     Hatch(Hatch),
 }
 
+impl Display for Object {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        // write!(f, "{}", self.core)
+        match self {
+            Object::Curve(x) => write!(f, "{x}"),
+            Object::Point(x) => write!(f, "{x}"),
+            Object::Rectangle(x) => write!(f, "{x}"),
+            Object::Circle(x) => write!(f, "{x}"),
+            Object::Ellipse(x) => write!(f, "{x}"),
+            Object::Polygon(x) => write!(f, "{x}"),
+            Object::Hatch(x) => write!(f, "{x}"),
+        }
+    }
+}
+
 impl Translate for Object {
     fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
         match self {
@@ -262,6 +293,121 @@ impl ObjectModifier {
         self.default = 255.into();
         self.modifiers.correction += delta;
     }
+
+    pub fn correction(&self) -> Coordinate {
+        self.modifiers.correction
+    }
+
+    pub fn scale(&self) -> Coordinate {
+        Coordinate {
+            x: self.modifiers.x_scale,
+            y: self.modifiers.y_scale,
+        }
+    }
+
+    pub fn skew(&self) -> Coordinate {
+        Coordinate {
+            x: self.modifiers.y_skew.asin(), // Radians from X axis
+            y: self.modifiers.x_skew.asin(), // Radians from Y axis
+        }
+    }
+
+    /// Returns origin, width, and height of bounding box given two rectangular corners
+    pub fn corrected(&self, pt1: Coordinate, pt2: Coordinate) -> (Coordinate, f64, f64) {
+        debug!(
+            "Skew degree from X axis: {:.2}",
+            self.modifiers.x_skew.asin().to_degrees()
+        );
+        debug!(
+            "Skew degree from Y axis: {:.2}",
+            self.modifiers.y_skew.asin().to_degrees()
+        );
+
+        // Calculate 4 points of drawn rectangle
+        let drawn_pts: Vec<Coordinate> = vec![
+            Coordinate {
+                x: pt1.min(pt2).x,
+                y: pt1.min(pt2).y,
+            },
+            Coordinate {
+                x: pt1.min(pt2).x,
+                y: pt1.max(pt2).y,
+            },
+            Coordinate {
+                x: pt1.max(pt2).x,
+                y: pt1.max(pt2).y,
+            },
+            Coordinate {
+                x: pt1.max(pt2).x,
+                y: pt1.min(pt2).y,
+            },
+        ];
+
+        debug!(
+            "Drawn points: {}",
+            drawn_pts
+                .iter()
+                .map(|x| format!("{x}"))
+                .collect_vec()
+                .join(", ")
+        );
+
+        // Apply skew to 4 points of drawn rectangle
+        let skewed_pts: Vec<Coordinate> = drawn_pts
+            .iter()
+            .map(|pt| Coordinate {
+                x: pt.x + pt.y * self.modifiers.x_skew.asin().tan(),
+                y: pt.y + pt.x * self.modifiers.y_skew.asin().tan(),
+            })
+            .collect_vec();
+
+        debug!(
+            "Skewed points: {}",
+            skewed_pts
+                .iter()
+                .map(|x| format!("{x}"))
+                .collect_vec()
+                .join(", ")
+        );
+
+        // Apply correction
+        let corrected_pts: Vec<Coordinate> = skewed_pts
+            .iter()
+            .map(|pt| *pt * self.scale() + self.correction())
+            .collect_vec();
+
+        debug!(
+            "Skewed points (corrected): {}",
+            corrected_pts
+                .iter()
+                .map(|x| format!("{x}"))
+                .collect_vec()
+                .join(", ")
+        );
+
+        // Calculate bounds
+        let min_x: f64 = corrected_pts.iter().fold(f64::MAX, |acc, pt| acc.min(pt.x));
+        let max_x: f64 = corrected_pts.iter().fold(f64::MIN, |acc, pt| acc.max(pt.x));
+        let min_y: f64 = corrected_pts.iter().fold(f64::MAX, |acc, pt| acc.min(pt.y));
+        let max_y: f64 = corrected_pts.iter().fold(f64::MIN, |acc, pt| acc.max(pt.y));
+
+        // Compute corrected min/max bounding box
+        let bound_a: Coordinate = Coordinate { x: min_x, y: min_y };
+        let bound_b: Coordinate = Coordinate { x: max_x, y: max_y };
+
+        debug!("Skewed bounding box (corrected): {}, {}", bound_a, bound_b);
+
+        // Calculate origin of bounding box
+        let origin: Coordinate = (bound_a + bound_b) / Coordinate::from((2.0, 2.0));
+
+        // Calculate width and height of bounding box
+        let Coordinate {
+            x: width,
+            y: height,
+        } = bound_b - bound_a;
+
+        (origin, width, height)
+    }
 }
 
 impl Default for ObjectModifier {
@@ -279,12 +425,10 @@ impl Default for ObjectModifier {
 ))]
 pub struct ScaleValues {
     pub x_scale: f64, // X scaled ratio on drawn object
-    #[br(assert(_unknown_1 == 0.0))]
-    _unknown_1: f64,
+    pub y_skew: f64,
     #[br(assert(_unknown_2 == 0.0))]
     _unknown_2: f64,
-    #[br(assert(_unknown_3 == 0.0))]
-    _unknown_3: f64,
+    pub x_skew: f64,
     pub y_scale: f64, // Y scaled ratio on drawn object
     #[br(assert(_unknown_4 == 0.0))]
     _unknown_4: f64,
@@ -297,9 +441,9 @@ impl Default for ScaleValues {
     fn default() -> Self {
         Self {
             x_scale: 1.0.into(),
-            _unknown_1: 0.0.into(),
+            y_skew: 0.0.into(),
             _unknown_2: 0.0.into(),
-            _unknown_3: 0.0.into(),
+            x_skew: 0.0.into(),
             y_scale: 1.0.into(),
             _unknown_4: 0.0.into(),
             correction: (0.0, 0.0).into(),
@@ -314,6 +458,8 @@ impl Debug for ScaleValues {
         f.debug_struct("ScaleValues")
             .field("x_scale", &self.x_scale)
             .field("y_scale", &self.y_scale)
+            .field("x_skew", &self.x_skew)
+            .field("y_skew", &self.y_skew)
             .field("correction", &self.correction)
             .finish()
     }

+ 92 - 63
src/ezcad/objects/polygon.rs

@@ -1,63 +1,92 @@
-use std::fmt::Debug;
-
-use binrw::{BinRead, BinWrite};
-use diff::Diff;
-
-use crate::{
-    field_of::FieldOf,
-    types::{Coordinate, ObjectType, F64, U32},
-};
-
-use super::{ObjectCore, ObjectModifier, Translate};
-
-#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
-#[diff(attr(
-    #[derive(Debug, PartialEq)]
-))]
-pub struct Polygon {
-    pub core: ObjectCore,
-    #[brw(magic(10u32))] // Number of following fields in struct
-    pub invert_shape: U32,
-    pub drawn_corner_a: FieldOf<Coordinate>,
-    pub drawn_corner_b: FieldOf<Coordinate>,
-    pub offset_cx: F64,
-    pub offset_cy: F64,
-    pub offset_dx: F64,
-    pub offset_dy: F64,
-    pub edges: U32,
-    pub modifier: ObjectModifier,
-}
-
-impl Default for Polygon {
-    fn default() -> Self {
-        Self {
-            core: ObjectCore::default(ObjectType::Polygon),
-            invert_shape: 1.into(),
-            drawn_corner_a: Coordinate { x: 0.0, y: 0.0 }.into(),
-            drawn_corner_b: Coordinate { x: 0.0, y: 0.0 }.into(),
-            offset_cx: 0.0.into(),
-            offset_cy: 0.0.into(),
-            offset_dx: 0.0.into(),
-            offset_dy: 0.0.into(),
-            edges: 6.into(),
-            modifier: ObjectModifier::default(),
-        }
-    }
-}
-
-impl Translate for Polygon {
-    fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
-        origin.map(|origin| {
-            let delta: Coordinate = origin - *self.core.origin;
-            self.modifier.move_relative(delta);
-        });
-        self.core.move_absolute(origin, z);
-    }
-
-    fn move_relative(&mut self, delta: Option<Coordinate>, z: Option<f64>) {
-        delta.map(|delta| {
-            self.modifier.move_relative(delta);
-        });
-        self.core.move_relative(delta, z);
-    }
-}
+use std::fmt::{Debug, Display};
+
+use binrw::{BinRead, BinWrite};
+use diff::Diff;
+
+use crate::{
+    field_of::FieldOf,
+    types::{Coordinate, ObjectType, F64, U32},
+};
+
+use super::{ObjectCore, ObjectModifier, Translate};
+
+#[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
+#[diff(attr(
+    #[derive(Debug, PartialEq)]
+))]
+pub struct Polygon {
+    pub core: ObjectCore,
+    #[brw(magic(10u32))] // Number of following fields in struct
+    pub invert_shape: U32,
+    pub drawn_corner_a: FieldOf<Coordinate>,
+    pub drawn_corner_b: FieldOf<Coordinate>,
+    pub offset_cx: F64,
+    pub offset_cy: F64,
+    pub offset_dx: F64,
+    pub offset_dy: F64,
+    pub edges: U32,
+    pub modifier: ObjectModifier,
+}
+
+impl Display for Polygon {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let width: f64 = (self.drawn_corner_a.x.max(self.drawn_corner_b.x)
+            - self.drawn_corner_a.x.min(self.drawn_corner_b.x))
+            * self.modifier.modifiers.x_scale;
+        let height: f64 = (self.drawn_corner_a.y.max(self.drawn_corner_b.y)
+            - self.drawn_corner_a.y.min(self.drawn_corner_b.y))
+            * self.modifier.modifiers.y_scale;
+
+        let drawn_origin: Coordinate =
+            (*self.drawn_corner_a + *self.drawn_corner_b) / Coordinate { x: 2.0, y: 2.0 };
+
+        write!(
+            f,
+            "{}, Origin: {}, Width: {:.2}, Height: {:.2}, Inverted: {}, Offset CX: {:.2}, Offset CY: {:.2}, Offset DX: {:.2}, Offset: DY: {:.2}, Edges: {}",
+            self.core,
+            self.modifier.correction() + drawn_origin * self.modifier.scale(),
+            width,
+            height,
+            *self.invert_shape != 0,
+            *self.offset_cx,
+            *self.offset_cy,
+            *self.offset_dx,
+            *self.offset_dy,
+            *self.edges,
+        )
+    }
+}
+
+impl Default for Polygon {
+    fn default() -> Self {
+        Self {
+            core: ObjectCore::default(ObjectType::Polygon),
+            invert_shape: 1.into(),
+            drawn_corner_a: Coordinate { x: 0.0, y: 0.0 }.into(),
+            drawn_corner_b: Coordinate { x: 0.0, y: 0.0 }.into(),
+            offset_cx: 0.0.into(),
+            offset_cy: 0.0.into(),
+            offset_dx: 0.0.into(),
+            offset_dy: 0.0.into(),
+            edges: 6.into(),
+            modifier: ObjectModifier::default(),
+        }
+    }
+}
+
+impl Translate for Polygon {
+    fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
+        origin.map(|origin| {
+            let delta: Coordinate = origin - *self.core.origin;
+            self.modifier.move_relative(delta);
+        });
+        self.core.move_absolute(origin, z);
+    }
+
+    fn move_relative(&mut self, delta: Option<Coordinate>, z: Option<f64>) {
+        delta.map(|delta| {
+            self.modifier.move_relative(delta);
+        });
+        self.core.move_relative(delta, z);
+    }
+}

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

@@ -1,7 +1,8 @@
-use std::fmt::Debug;
+use std::fmt::{Debug, Display};
 
 use binrw::{BinRead, BinWrite};
 use diff::Diff;
+use log::warn;
 
 use crate::{
     field_of::FieldOf,
@@ -26,6 +27,27 @@ pub struct Rectangle {
     pub modifier: ObjectModifier,
 }
 
+impl Display for Rectangle {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        let (origin, width, height) = self
+            .modifier
+            .corrected(*self.drawn_corner_a, *self.drawn_corner_b);
+
+        if format!("{}", origin) != format!("{}", *self.core.origin) {
+            warn!(
+                "Origin mismatch! Core: {}, Calculated: {}",
+                *self.core.origin, origin
+            );
+        }
+
+        write!(
+            f,
+            "{}, Origin: {}, Width: {:.2}, Height: {:.2}",
+            self.core, origin, width, height,
+        )
+    }
+}
+
 impl Default for Rectangle {
     fn default() -> Self {
         Self {

+ 16 - 0
src/ezcad/types.rs

@@ -212,6 +212,22 @@ impl From<(f64, f64)> for Coordinate {
     }
 }
 
+impl Coordinate {
+    pub fn min(&self, rhs: Coordinate) -> Coordinate {
+        Coordinate {
+            x: self.x.min(rhs.x),
+            y: self.y.min(rhs.y),
+        }
+    }
+
+    pub fn max(&self, rhs: Coordinate) -> Coordinate {
+        Coordinate {
+            x: self.x.max(rhs.x),
+            y: self.y.max(rhs.y),
+        }
+    }
+}
+
 #[derive(Clone, Debug, Diff, PartialEq, BinRead, BinWrite, strum::Display)]
 #[diff(attr(
     #[derive(Debug, PartialEq)]

+ 5 - 4
src/main.rs

@@ -227,10 +227,12 @@ fn main() {
                     .get(layer_index)
                     .expect("Invalid layer index");
                 let object: &Object = layer.objects.get(obj_index).expect("Invalid object index");
-                info!("Layer {}, Object {}: {:?}", layer_index, obj_index, object);
                 let pen_index: u32 = *object.core().pen;
-                trace!(
-                    "Pen #{}: {}",
+                info!(
+                    "Layer {}, Object {}:\n{}\nPen: #{}: {}",
+                    layer_index,
+                    obj_index,
+                    object,
                     pen_index,
                     file.pens_offset
                         .data
@@ -238,7 +240,6 @@ fn main() {
                         .get(pen_index as usize)
                         .expect("Invalid pen index in object")
                 );
-                // TODO: object info
             });
         }
         SubCommands::Apply(args) => {