2 Commits 561205cadd ... 0a1ed95d37

Author SHA1 Message Date
  Kevin Lee 0a1ed95d37 Refactor to use subcommands 6 months ago
  Kevin Lee d7a776caee Add pen querying 10 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"
 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]]
 name = "humantime"
 version = "2.1.0"
@@ -330,6 +339,7 @@ dependencies = [
  "clap-verbosity-flag",
  "diff-struct",
  "env_logger",
+ "human-repr",
  "itertools",
  "log",
  "modular-bitfield",

+ 1 - 0
Cargo.toml

@@ -19,6 +19,7 @@ clap = { version = "4.4.11", features = ["derive"] }
 clap-verbosity-flag = "2.1.1"
 diff-struct = "0.5.3"
 env_logger = "0.10.1"
+human-repr = { version = "1.1.0", features = ["serde"] }
 itertools = "0.12.0"
 log = "0.4.20"
 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.
 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
 
 ``` text
 Usage: minilase.exe [OPTIONS] --input <INPUT>
 
 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

+ 3 - 3
src/config/object.rs

@@ -227,8 +227,8 @@ impl ObjectOperation {
                     // Build new hatch object (no hatch lines)
                     Object::Hatch(Hatch {
                         core: ObjectCore {
-                            origin: object.core().origin,
-                            z: object.core().z,
+                            origin: object.core_mut().origin,
+                            z: object.core_mut().z,
                             ..ObjectCore::default(ObjectType::Hatch)
                         },
                         outline: vec![object].into(),
@@ -285,7 +285,7 @@ impl ObjectOperation {
 
                         debug!(
                             "Adding new object at {} with pen #{}",
-                            object.core().origin,
+                            object.core_mut().origin,
                             pen
                         );
                         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 {
             Object::Curve(x) => &mut x.core,
             Object::Point(x) => &mut x.core,
@@ -234,6 +234,18 @@ impl Object {
             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)]

+ 14 - 0
src/ezcad/pen.rs

@@ -7,6 +7,7 @@ use std::{
 
 use binrw::{BinRead, BinWrite, BinWriterExt, FilePtr64};
 use diff::Diff;
+use human_repr::HumanCount;
 use log::{error, warn};
 
 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
 #[cfg(not(feature = "default-debug"))]
 impl Debug for Pen {

+ 161 - 84
src/main.rs

@@ -6,11 +6,11 @@ use std::{
 };
 
 use binrw::{BinRead, BinWrite, BinWriterExt};
-use clap::Parser;
+use clap::{Args, Parser, Subcommand};
 use clap_verbosity_flag::{InfoLevel, Verbosity};
 use diff::Diff;
 use env_logger::Target;
-use ezcad::file::EzCadHeader;
+use ezcad::{file::EzCadHeader, layer::Layer, objects::Object};
 use log::{info, trace, warn};
 
 use crate::config::{Config, Operations};
@@ -19,24 +19,58 @@ mod config;
 
 #[derive(Debug, Parser)]
 struct Cli {
-    /// Input file to parse
+    #[command(subcommand)]
+    command: SubCommands,
+
+    /// Input .mlp file to parse
     #[arg(short, long)]
     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
     #[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)]
-    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
     #[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() {
@@ -77,84 +111,127 @@ fn main() {
         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
                 .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");
+            });
+        }
+    }
 }