2 Commits 561205cadd ... 0a1ed95d37

Author SHA1 Message Date
  Kevin Lee 0a1ed95d37 Refactor to use subcommands 5 months ago
  Kevin Lee d7a776caee Add pen querying 8 months ago
7 changed files with 217 additions and 95 deletions
  1. 10 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 15 7
      README.md
  4. 3 3
      src/config/object.rs
  5. 13 1
      src/ezcad/objects/mod.rs
  6. 14 0
      src/ezcad/pen.rs
  7. 161 84
      src/main.rs

+ 10 - 0
Cargo.lock

@@ -255,6 +255,15 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
 checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
 
 
+[[package]]
+name = "human-repr"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f58b778a5761513caf593693f8951c97a5b610841e754788400f32102eefdff1"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 [[package]]
 name = "humantime"
 name = "humantime"
 version = "2.1.0"
 version = "2.1.0"
@@ -330,6 +339,7 @@ dependencies = [
  "clap-verbosity-flag",
  "clap-verbosity-flag",
  "diff-struct",
  "diff-struct",
  "env_logger",
  "env_logger",
+ "human-repr",
  "itertools",
  "itertools",
  "log",
  "log",
  "modular-bitfield",
  "modular-bitfield",

+ 1 - 0
Cargo.toml

@@ -19,6 +19,7 @@ clap = { version = "4.4.11", features = ["derive"] }
 clap-verbosity-flag = "2.1.1"
 clap-verbosity-flag = "2.1.1"
 diff-struct = "0.5.3"
 diff-struct = "0.5.3"
 env_logger = "0.10.1"
 env_logger = "0.10.1"
+human-repr = { version = "1.1.0", features = ["serde"] }
 itertools = "0.12.0"
 itertools = "0.12.0"
 log = "0.4.20"
 log = "0.4.20"
 modular-bitfield = "0.11.2"
 modular-bitfield = "0.11.2"

+ 15 - 7
README.md

@@ -3,19 +3,27 @@
 This tool parses and patches Minilase EZCAD save files with values specified in a YAML config file.
 This tool parses and patches Minilase EZCAD save files with values specified in a YAML config file.
 The EZCAD format as implemented in this crate was reverse engineered from sample files, so not all formats or options are supported.
 The EZCAD format as implemented in this crate was reverse engineered from sample files, so not all formats or options are supported.
 
 
+TODO:
+
+* Write settings to output file by default
+* Figure out some sort of formula to sweep power density
+
 ## Usage
 ## Usage
 
 
 ``` text
 ``` text
 Usage: minilase.exe [OPTIONS] --input <INPUT>
 Usage: minilase.exe [OPTIONS] --input <INPUT>
 
 
 Options:
 Options:
-  -i, --input <INPUT>    Input file to parse
-  -d, --diff <DIFF>      File to diff input against
-  -o, --output <OUTPUT>  Output file to write to
-  -c, --config <CONFIG>  Configuration file
-  -v, --verbose...       Increase logging verbosity
-  -q, --quiet...         Decrease logging verbosity
-  -h, --help             Print help
+  -i, --input <INPUT>                Input file to parse
+  -d, --diff <DIFF>                  File to diff input against
+  -o, --output <OUTPUT>              Output file to write to
+  -c, --config <CONFIG>              Configuration file
+  -p, --pen <PEN>                    Print pen info
+  -l, --object-layer <OBJECT_LAYER>  Layer of object to print info
+  -b, --object <OBJECT>              Print object info
+  -v, --verbose...                   Increase logging verbosity
+  -q, --quiet...                     Decrease logging verbosity
+  -h, --help                         Print help
 ```
 ```
 
 
 ## Configuration File
 ## Configuration File

+ 3 - 3
src/config/object.rs

@@ -227,8 +227,8 @@ impl ObjectOperation {
                     // Build new hatch object (no hatch lines)
                     // Build new hatch object (no hatch lines)
                     Object::Hatch(Hatch {
                     Object::Hatch(Hatch {
                         core: ObjectCore {
                         core: ObjectCore {
-                            origin: object.core().origin,
-                            z: object.core().z,
+                            origin: object.core_mut().origin,
+                            z: object.core_mut().z,
                             ..ObjectCore::default(ObjectType::Hatch)
                             ..ObjectCore::default(ObjectType::Hatch)
                         },
                         },
                         outline: vec![object].into(),
                         outline: vec![object].into(),
@@ -285,7 +285,7 @@ impl ObjectOperation {
 
 
                         debug!(
                         debug!(
                             "Adding new object at {} with pen #{}",
                             "Adding new object at {} with pen #{}",
-                            object.core().origin,
+                            object.core_mut().origin,
                             pen
                             pen
                         );
                         );
                         new_obj.push(object);
                         new_obj.push(object);

+ 13 - 1
src/ezcad/objects/mod.rs

@@ -223,7 +223,7 @@ impl Object {
         }
         }
     }
     }
 
 
-    pub fn core(&mut self) -> &mut ObjectCore {
+    pub fn core_mut(&mut self) -> &mut ObjectCore {
         match self {
         match self {
             Object::Curve(x) => &mut x.core,
             Object::Curve(x) => &mut x.core,
             Object::Point(x) => &mut x.core,
             Object::Point(x) => &mut x.core,
@@ -234,6 +234,18 @@ impl Object {
             Object::Hatch(x) => &mut x.core,
             Object::Hatch(x) => &mut x.core,
         }
         }
     }
     }
+
+    pub fn core(&self) -> &ObjectCore {
+        match self {
+            Object::Curve(x) => &x.core,
+            Object::Point(x) => &x.core,
+            Object::Rectangle(x) => &x.core,
+            Object::Circle(x) => &x.core,
+            Object::Ellipse(x) => &x.core,
+            Object::Polygon(x) => &x.core,
+            Object::Hatch(x) => &x.core,
+        }
+    }
 }
 }
 
 
 #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]
 #[derive(BinRead, BinWrite, Clone, Debug, Diff, PartialEq)]

+ 14 - 0
src/ezcad/pen.rs

@@ -7,6 +7,7 @@ use std::{
 
 
 use binrw::{BinRead, BinWrite, BinWriterExt, FilePtr64};
 use binrw::{BinRead, BinWrite, BinWriterExt, FilePtr64};
 use diff::Diff;
 use diff::Diff;
+use human_repr::HumanCount;
 use log::{error, warn};
 use log::{error, warn};
 
 
 use crate::{
 use crate::{
@@ -171,6 +172,19 @@ impl Pen {
     }
     }
 }
 }
 
 
+impl std::fmt::Display for Pen {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "Speed: {}mm/s, Frequency: {}, Power: {}%, Pulse Width: {}ns",
+            self.speed,
+            self.frequency.human_count("Hz"),
+            self.power,
+            self.pulse_width
+        )
+    }
+}
+
 // 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 Pen {
 impl Debug for Pen {

+ 161 - 84
src/main.rs

@@ -6,11 +6,11 @@ use std::{
 };
 };
 
 
 use binrw::{BinRead, BinWrite, BinWriterExt};
 use binrw::{BinRead, BinWrite, BinWriterExt};
-use clap::Parser;
+use clap::{Args, Parser, Subcommand};
 use clap_verbosity_flag::{InfoLevel, Verbosity};
 use clap_verbosity_flag::{InfoLevel, Verbosity};
 use diff::Diff;
 use diff::Diff;
 use env_logger::Target;
 use env_logger::Target;
-use ezcad::file::EzCadHeader;
+use ezcad::{file::EzCadHeader, layer::Layer, objects::Object};
 use log::{info, trace, warn};
 use log::{info, trace, warn};
 
 
 use crate::config::{Config, Operations};
 use crate::config::{Config, Operations};
@@ -19,24 +19,58 @@ mod config;
 
 
 #[derive(Debug, Parser)]
 #[derive(Debug, Parser)]
 struct Cli {
 struct Cli {
-    /// Input file to parse
+    #[command(subcommand)]
+    command: SubCommands,
+
+    /// Input .mlp file to parse
     #[arg(short, long)]
     #[arg(short, long)]
     input: PathBuf,
     input: PathBuf,
 
 
+    #[command(flatten)]
+    verbose: Verbosity<InfoLevel>,
+}
+
+#[derive(Debug, Subcommand)]
+enum SubCommands {
+    Diff(DiffCmd),
+    Query(QueryCmd),
+    Apply(ApplyConfig),
+}
+
+/// Diff two .mlp files and print differences between the two
+#[derive(Debug, Args)]
+struct DiffCmd {
     /// File to diff input against
     /// File to diff input against
     #[arg(short, long)]
     #[arg(short, long)]
-    diff: Option<PathBuf>,
+    diff_file: PathBuf,
+}
 
 
-    /// Output file to write to
+/// Queries input .mlp file for pen or object info
+#[derive(Debug, Args)]
+struct QueryCmd {
+    /// Print pen info
     #[arg(short, long)]
     #[arg(short, long)]
-    output: Option<PathBuf>,
+    pen: Option<Vec<usize>>,
+
+    /// Print object info
+    #[arg(short, long)]
+    object: Option<usize>,
+
+    /// Object layer to query object on
+    #[arg(short = 'b', long)]
+    object_layer: Option<usize>,
+}
 
 
+/// Applies configuration YAML to input .mlp file
+#[derive(Debug, Args)]
+struct ApplyConfig {
     /// Configuration file
     /// Configuration file
     #[arg(short, long)]
     #[arg(short, long)]
-    config: Option<PathBuf>,
+    config: PathBuf,
 
 
-    #[command(flatten)]
-    verbose: Verbosity<InfoLevel>,
+    /// Output file to write to
+    #[arg(short, long)]
+    output: Option<PathBuf>,
 }
 }
 
 
 fn main() {
 fn main() {
@@ -77,84 +111,127 @@ fn main() {
         warn!("Re-encoded input file does not match original file!");
         warn!("Re-encoded input file does not match original file!");
     }
     }
 
 
-    // Print info on pens with non-default settings
-    for (index, pen) in file
-        .pens_offset
-        .data
-        .pens
-        .iter()
-        .filter(|x| *x.use_default == 0)
-        .enumerate()
-    {
-        trace!("Pen {}: {:#?}", index, pen);
-    }
+    match cli.command {
+        SubCommands::Diff(args) => {
+            info!(
+                "Processing diff file '{}'",
+                args.diff_file.to_string_lossy()
+            );
+            let mut diff: File = File::open(args.diff_file).expect("Failed to open diff file");
+            let diff_file: EzCadHeader =
+                EzCadHeader::read_le(&mut diff).expect("Failed to parse diff file as EZCAD format");
+
+            // Diff pens
+            info!(
+                "{:#?}",
+                file.pens_offset
+                    .data
+                    .pens
+                    .diff(&diff_file.pens_offset.data.pens)
+            );
 
 
-    // Print all objects
-    for (layer_index, layer) in file.layers_offset.iter().enumerate() {
-        for (object_index, object) in layer.objects.iter().enumerate() {
-            trace!(
-                "Layer {}, Object {}: {:#?}",
-                layer_index,
-                object_index,
-                object
+            // Diff objects
+            info!(
+                "{:#?}",
+                file.layers_offset
+                    .value
+                    .diff(&diff_file.layers_offset.value)
             );
             );
         }
         }
-    }
-
-    // Process diff
-    cli.diff.map(|diff| {
-        info!("Processing diff file '{}'", diff.to_string_lossy());
-        let mut diff: File = File::open(diff).expect("Failed to open diff file");
-        let diff_file: EzCadHeader =
-            EzCadHeader::read_le(&mut diff).expect("Failed to parse diff file as EZCAD format");
-
-        // Diff pens
-        info!(
-            "{:#?}",
-            file.pens_offset
+        SubCommands::Query(args) => {
+            // Print info on pens with non-default settings
+            for (index, pen) in file
+                .pens_offset
                 .data
                 .data
                 .pens
                 .pens
-                .diff(&diff_file.pens_offset.data.pens)
-        );
-
-        // Diff objects
-        info!(
-            "{:#?}",
-            file.layers_offset
-                .value
-                .diff(&diff_file.layers_offset.value)
-        );
-    });
-
-    // Process config
-    cli.config.map(|config| {
-        info!("Processing config file '{}'", config.to_string_lossy());
-        let config: String = std::fs::read_to_string(config).expect("Failed to open config file");
-        let config: Config = serde_yaml::from_str(&config).expect("Failed to parse config file");
-
-        // Patch pen settings
-        let time: Instant = Instant::now();
-        config
-            .ops
-            .apply(&mut file.pens_offset.data.pens, &mut file.layers_offset);
-        trace!("Config processing time: {:?}", time.elapsed());
-    });
-
-    // Process output
-    cli.output.map(|output| {
-        info!("Writing output file '{}'", output.to_string_lossy());
-        // Serialize to memory buffer for perf
-        let mut buffer: Cursor<Vec<u8>> = Cursor::new(vec![]);
-        let time: Instant = Instant::now();
-        buffer
-            .write_le(&file)
-            .expect("Failed to serialize contents as EZCAD format");
-        trace!("Output file encode time: {:?}", time.elapsed());
-
-        // Write buffer to output file
-        let mut output: File = File::create(output).expect("Failed to open output file");
-        output
-            .write_all(buffer.into_inner().as_slice())
-            .expect("Failed to write to output file");
-    });
+                .iter()
+                .filter(|x| *x.use_default == 0)
+                .enumerate()
+            {
+                trace!("Pen {}: {:#?}", index, pen);
+            }
+
+            // Print all objects
+            for (layer_index, layer) in file.layers_offset.iter().enumerate() {
+                for (object_index, object) in layer.objects.iter().enumerate() {
+                    trace!(
+                        "Layer {}, Object {}: {:#?}",
+                        layer_index,
+                        object_index,
+                        object
+                    );
+                }
+            }
+
+            // Process pen query
+            args.pen.map(|pens| {
+                for pen in pens {
+                    info!(
+                        "Pen #{}: {}",
+                        pen,
+                        file.pens_offset
+                            .data
+                            .pens
+                            .get(pen)
+                            .expect("Invalid pen index")
+                    );
+                }
+            });
+
+            // Process object query
+            args.object.map(|obj_index| {
+                let layer_index: usize = args.object_layer.unwrap_or(0);
+                let layer: &Layer = file
+                    .layers_offset
+                    .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 #{}: {}",
+                    pen_index,
+                    file.pens_offset
+                        .data
+                        .pens
+                        .get(pen_index as usize)
+                        .expect("Invalid pen index in object")
+                );
+                // TODO: object info
+            });
+        }
+        SubCommands::Apply(args) => {
+            // Process config
+            info!("Processing config file '{}'", args.config.to_string_lossy());
+            let config: String =
+                std::fs::read_to_string(args.config).expect("Failed to open config file");
+            let config: Config =
+                serde_yaml::from_str(&config).expect("Failed to parse config file");
+
+            // Patch pen settings
+            let time: Instant = Instant::now();
+            config
+                .ops
+                .apply(&mut file.pens_offset.data.pens, &mut file.layers_offset);
+            trace!("Config processing time: {:?}", time.elapsed());
+
+            // Process output
+            args.output.map(|output| {
+                info!("Writing output file '{}'", output.to_string_lossy());
+                // Serialize to memory buffer for perf
+                let mut buffer: Cursor<Vec<u8>> = Cursor::new(vec![]);
+                let time: Instant = Instant::now();
+                buffer
+                    .write_le(&file)
+                    .expect("Failed to serialize contents as EZCAD format");
+                trace!("Output file encode time: {:?}", time.elapsed());
+
+                // Write buffer to output file
+                let mut output: File = File::create(output).expect("Failed to open output file");
+                output
+                    .write_all(buffer.into_inner().as_slice())
+                    .expect("Failed to write to output file");
+            });
+        }
+    }
 }
 }