|
@@ -1,5 +1,5 @@
|
|
use std::{
|
|
use std::{
|
|
- fmt::Debug,
|
|
|
|
|
|
+ fmt::{Debug, Display},
|
|
fs::File,
|
|
fs::File,
|
|
io::{Cursor, Read, Write},
|
|
io::{Cursor, Read, Write},
|
|
path::PathBuf,
|
|
path::PathBuf,
|
|
@@ -7,6 +7,8 @@ use std::{
|
|
|
|
|
|
use binrw::{BinRead, BinWrite, BinWriterExt};
|
|
use binrw::{BinRead, BinWrite, BinWriterExt};
|
|
use diff::Diff;
|
|
use diff::Diff;
|
|
|
|
+use itertools::Itertools;
|
|
|
|
+use log::debug;
|
|
use modular_bitfield::{
|
|
use modular_bitfield::{
|
|
bitfield,
|
|
bitfield,
|
|
specifiers::{B1, B14},
|
|
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)
|
|
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
|
|
// Custom Debug implementation to only print known fields
|
|
#[cfg(not(feature = "default-debug"))]
|
|
#[cfg(not(feature = "default-debug"))]
|
|
impl Debug for ObjectCore {
|
|
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(
|
|
#[diff(attr(
|
|
#[derive(Debug, PartialEq)]
|
|
#[derive(Debug, PartialEq)]
|
|
))]
|
|
))]
|
|
@@ -156,6 +172,21 @@ pub enum Object {
|
|
Hatch(Hatch),
|
|
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 {
|
|
impl Translate for Object {
|
|
fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
|
|
fn move_absolute(&mut self, origin: Option<Coordinate>, z: Option<f64>) {
|
|
match self {
|
|
match self {
|
|
@@ -262,6 +293,121 @@ impl ObjectModifier {
|
|
self.default = 255.into();
|
|
self.default = 255.into();
|
|
self.modifiers.correction += delta;
|
|
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 {
|
|
impl Default for ObjectModifier {
|
|
@@ -279,12 +425,10 @@ impl Default for ObjectModifier {
|
|
))]
|
|
))]
|
|
pub struct ScaleValues {
|
|
pub struct ScaleValues {
|
|
pub x_scale: f64, // X scaled ratio on drawn object
|
|
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))]
|
|
#[br(assert(_unknown_2 == 0.0))]
|
|
_unknown_2: f64,
|
|
_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
|
|
pub y_scale: f64, // Y scaled ratio on drawn object
|
|
#[br(assert(_unknown_4 == 0.0))]
|
|
#[br(assert(_unknown_4 == 0.0))]
|
|
_unknown_4: f64,
|
|
_unknown_4: f64,
|
|
@@ -297,9 +441,9 @@ impl Default for ScaleValues {
|
|
fn default() -> Self {
|
|
fn default() -> Self {
|
|
Self {
|
|
Self {
|
|
x_scale: 1.0.into(),
|
|
x_scale: 1.0.into(),
|
|
- _unknown_1: 0.0.into(),
|
|
|
|
|
|
+ y_skew: 0.0.into(),
|
|
_unknown_2: 0.0.into(),
|
|
_unknown_2: 0.0.into(),
|
|
- _unknown_3: 0.0.into(),
|
|
|
|
|
|
+ x_skew: 0.0.into(),
|
|
y_scale: 1.0.into(),
|
|
y_scale: 1.0.into(),
|
|
_unknown_4: 0.0.into(),
|
|
_unknown_4: 0.0.into(),
|
|
correction: (0.0, 0.0).into(),
|
|
correction: (0.0, 0.0).into(),
|
|
@@ -314,6 +458,8 @@ impl Debug for ScaleValues {
|
|
f.debug_struct("ScaleValues")
|
|
f.debug_struct("ScaleValues")
|
|
.field("x_scale", &self.x_scale)
|
|
.field("x_scale", &self.x_scale)
|
|
.field("y_scale", &self.y_scale)
|
|
.field("y_scale", &self.y_scale)
|
|
|
|
+ .field("x_skew", &self.x_skew)
|
|
|
|
+ .field("y_skew", &self.y_skew)
|
|
.field("correction", &self.correction)
|
|
.field("correction", &self.correction)
|
|
.finish()
|
|
.finish()
|
|
}
|
|
}
|