3 Commits 7b34880229 ... cf75b3bcf4

Author SHA1 Message Date
  Kevin Lee cf75b3bcf4 Add upper bound on randomization attempts 3 months ago
  Kevin Lee 19aa6c19f8 Validate randomized pen settings 3 months ago
  Kevin Lee 914d7bc01a Add pen setting validation 3 months ago
4 changed files with 187 additions and 38 deletions
  1. 1 1
      README.md
  2. 92 36
      src/config/pen.rs
  3. 50 1
      src/ezcad/pen.rs
  4. 44 0
      src/ezcad/types.rs

+ 1 - 1
README.md

@@ -95,7 +95,7 @@ Op:
     Speed: [100, 1000, 100] # [min, max, step]
     Power: [10, 100, 5] # [min, max, step]
     Frequency: [20000, 100000, 1000] # [min, max, step]
-    PulseWidth: [2, 250, 2] # [min, max, step]
+    PulseWidth: [2, 350, 2] # [min, max, step]
 ```
 
 Exporting a pen to a file:

+ 92 - 36
src/config/pen.rs

@@ -106,6 +106,7 @@ impl PatchPen {
         debug!("Patching pen #{}", self.pen);
         let pen: &mut Pen = pens.get_mut(self.pen).expect("Invalid pen index");
         self.patch.patch(pen);
+        pen.valid_settings();
     }
 }
 
@@ -290,6 +291,8 @@ impl PatternField {
 
             // Always enable custom settings for pen
             *dst.use_default = 0;
+
+            dst.valid_settings();
         }
     }
 }
@@ -319,6 +322,30 @@ impl PatternPen {
     }
 }
 
+#[derive(Copy, Clone, Default, PartialEq)]
+struct RandomizedSetting {
+    speed: Option<f64>,
+    power: Option<f64>,
+    frequency: Option<u32>,
+    pulse_width: Option<PulseWidth>,
+}
+
+impl RandomizedSetting {
+    fn apply(&self, pen: &mut Pen) {
+        self.speed.map(|speed| *pen.speed = speed);
+        self.power.map(|power| *pen.power = power);
+        self.frequency.map(|freq| {
+            *pen.frequency = freq;
+            *pen.frequency_2 = freq.try_into().unwrap();
+        });
+        self.pulse_width.map(|pw| {
+            let pw: u32 = pw.into();
+            *pen.pulse_width = pw;
+            *pen.pulse_width_2 = pw.try_into().unwrap();
+        });
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "PascalCase")]
 pub struct RandomizePen {
@@ -338,53 +365,82 @@ impl RandomizePen {
             self.index + self.count - 1
         );
 
+        let mut generated: Vec<RandomizedSetting> = vec![];
+        const MAX_ATTEMPTS: usize = 1000;
+
         for (index, pen) in pens
             .iter_mut()
             .skip(self.index)
             .take(self.count)
             .enumerate()
         {
-            if let Some((min, max, step)) = self.speed {
-                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
-                let value: f64 = min + step * offset as f64;
-                debug!("Randomizing speed for pen #{} to {}", index, value);
-                *pen.speed = value;
-            }
+            for attempt in 0..=MAX_ATTEMPTS {
+                let mut setting: RandomizedSetting = RandomizedSetting::default();
+
+                if let Some((min, max, step)) = self.speed {
+                    let offset: usize =
+                        rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
+                    let value: f64 = min + step * offset as f64;
+                    debug!("Randomizing speed for pen #{} to {}", index, value);
+                    setting.speed = Some(value);
+                }
 
-            if let Some((min, max, step)) = self.power {
-                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
-                let value: f64 = min + step * offset as f64;
-                debug!("Randomizing power for pen #{} to {}", index, value);
-                *pen.power = value;
-            }
+                if let Some((min, max, step)) = self.power {
+                    let offset: usize =
+                        rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
+                    let value: f64 = min + step * offset as f64;
+                    debug!("Randomizing power for pen #{} to {}", index, value);
+                    setting.power = Some(value);
+                }
 
-            if let Some((min, max, step)) = self.frequency {
-                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
-                let value: u32 = min + step * offset as u32;
-                debug!("Randomizing frequency for pen #{} to {}", index, value);
-                *pen.frequency = value;
-                *pen.frequency_2 = value.try_into().unwrap();
-            }
+                if let Some((min, max, step)) = self.frequency {
+                    let offset: usize =
+                        rand::thread_rng().gen_range(0..=((max - min) / step) as usize);
+                    let value: u32 = min + step * offset as u32;
+                    debug!("Randomizing frequency for pen #{} to {}", index, value);
+                    setting.frequency = Some(value);
+                }
 
-            if let Some((min, max, step)) = self.pulse_width {
-                let mut pw = PulseWidth::iter();
-                let mut values: Vec<PulseWidth> = vec![pw.find(|x| *x == min).unwrap()];
-                values.extend(
-                    pw.skip(step - 1)
-                        .step_by(step)
-                        .take_while_inclusive(|x| *x != max)
-                        .collect_vec(),
-                );
+                if let Some((min, max, step)) = self.pulse_width {
+                    let mut pw = PulseWidth::iter();
+                    let mut values: Vec<PulseWidth> = vec![pw.find(|x| *x == min).unwrap()];
+                    values.extend(
+                        pw.skip(step - 1)
+                            .step_by(step)
+                            .take_while_inclusive(|x| *x != max)
+                            .collect_vec(),
+                    );
 
-                let width: &PulseWidth = values
-                    .choose_multiple(&mut rand::thread_rng(), 1)
-                    .next()
-                    .unwrap();
-                let value: u32 = (*width).into();
-                debug!("Randomizing pulse width for pen #{} to {}", index, value);
-                *pen.pulse_width = value;
-                *pen.pulse_width_2 = value.try_into().unwrap();
+                    let width: &PulseWidth = values
+                        .choose_multiple(&mut rand::thread_rng(), 1)
+                        .next()
+                        .unwrap();
+                    let value: u32 = (*width).into();
+                    debug!("Randomizing pulse width for pen #{} to {}", index, value);
+                    setting.pulse_width = Some(*width);
+                }
+
+                if !generated.contains(&setting) {
+                    generated.push(setting);
+                    setting.apply(pen);
+                    if !pen.valid_settings() {
+                        debug!("Retrying..");
+                    } else {
+                        break;
+                    }
+                } else {
+                    debug!("Duplicate random setting");
+                }
+
+                if attempt == MAX_ATTEMPTS {
+                    panic!(
+                        "Exceeded maximum number of {} randommization attempts",
+                        MAX_ATTEMPTS
+                    );
+                }
             }
+
+            pen.valid_settings();
         }
     }
 }

+ 50 - 1
src/ezcad/pen.rs

@@ -7,10 +7,11 @@ use std::{
 
 use binrw::{BinRead, BinWrite, BinWriterExt, FilePtr64};
 use diff::Diff;
+use log::{error, warn};
 
 use crate::{
     field_of::FieldOf,
-    types::{Bool, Field, Rgba, WString, WobbleType, F64, U32},
+    types::{Bool, Field, PulseWidth, Rgba, WString, WobbleType, F64, U32},
 };
 
 #[derive(BinRead, Debug)]
@@ -120,6 +121,54 @@ impl Pen {
             .write_all(buffer.into_inner().as_slice())
             .expect("Failed to write to output file");
     }
+
+    pub fn valid_settings(&self) -> bool {
+        let mut ret: bool = true;
+
+        if *self.frequency != *self.frequency_2 as u32 {
+            error!(
+                "Mismatch pen internal frequency setting: ({}, {:.3})",
+                *self.frequency, *self.frequency_2
+            );
+            ret = false;
+        }
+
+        if *self.pulse_width != *self.pulse_width_2 as u32 {
+            error!(
+                "Mismatch pen internal pulse width setting: ({}, {:.3})",
+                *self.pulse_width, *self.pulse_width_2
+            );
+            ret = false;
+        }
+
+        match PulseWidth::try_from(*self.pulse_width) {
+            Ok(pw) => match *self.frequency {
+                freq if freq < pw.min_freq() => {
+                    warn!(
+                        "Pen frequency of {} lower than pulse width minimum frequency of {}",
+                        *self.frequency,
+                        pw.min_freq()
+                    );
+                    ret = false;
+                }
+                freq if freq > pw.max_freq() => {
+                    warn!(
+                        "Pen frequency of {} higher than pulse width maximum frequency of {}",
+                        *self.frequency,
+                        pw.max_freq()
+                    );
+                    ret = false;
+                }
+                _ => (),
+            },
+            Err(_) => {
+                warn!("Invalid pen pulse width value: {}", *self.pulse_width);
+                ret = false;
+            }
+        }
+
+        ret
+    }
 }
 
 // Custom Debug implementation to only print known fields

+ 44 - 0
src/ezcad/types.rs

@@ -291,3 +291,47 @@ pub enum PulseWidth {
     Ns350 = 350,
     Ns500 = 500,
 }
+
+impl PulseWidth {
+    pub fn min_freq(&self) -> u32 {
+        match self {
+            PulseWidth::Ns2 => 850_000,
+            PulseWidth::Ns4 => 500_000,
+            PulseWidth::Ns6 => 320_000,
+            PulseWidth::Ns8 => 250_000,
+            PulseWidth::Ns12 => 170_000,
+            PulseWidth::Ns20 => 115_000,
+            PulseWidth::Ns30 => 90_000,
+            PulseWidth::Ns45 => 75_000,
+            PulseWidth::Ns60 => 65_000,
+            PulseWidth::Ns80 => 60_000,
+            PulseWidth::Ns100 => 45_000,
+            PulseWidth::Ns150 => 30_000,
+            PulseWidth::Ns200 => 25_000,
+            PulseWidth::Ns250 => 25_000,
+            PulseWidth::Ns350 => 25_000,
+            PulseWidth::Ns500 => 25_000,
+        }
+    }
+
+    pub fn max_freq(&self) -> u32 {
+        match self {
+            PulseWidth::Ns2 => 4_000_000,
+            PulseWidth::Ns4 => 4_000_000,
+            PulseWidth::Ns6 => 4_000_000,
+            PulseWidth::Ns8 => 4_000_000,
+            PulseWidth::Ns12 => 3_000_000,
+            PulseWidth::Ns20 => 3_000_000,
+            PulseWidth::Ns30 => 3_000_000,
+            PulseWidth::Ns45 => 2_000_000,
+            PulseWidth::Ns60 => 2_000_000,
+            PulseWidth::Ns80 => 2_000_000,
+            PulseWidth::Ns100 => 1_000_000,
+            PulseWidth::Ns150 => 1_000_000,
+            PulseWidth::Ns200 => 1_000_000,
+            PulseWidth::Ns250 => 900_000,
+            PulseWidth::Ns350 => 600_000,
+            PulseWidth::Ns500 => 500_000,
+        }
+    }
+}