Browse Source

Support randomizing pens

Kevin Lee 3 months ago
parent
commit
8a91cad412
5 changed files with 142 additions and 47 deletions
  1. 10 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 15 0
      README.md
  4. 49 47
      src/config/mod.rs
  5. 67 0
      src/config/pen.rs

+ 10 - 0
Cargo.lock

@@ -282,6 +282,15 @@ dependencies = [
  "windows-sys 0.48.0",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "1.0.10"
@@ -321,6 +330,7 @@ dependencies = [
  "clap-verbosity-flag",
  "diff-struct",
  "env_logger",
+ "itertools",
  "log",
  "modular-bitfield",
  "num",

+ 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"
+itertools = "0.12.0"
 log = "0.4.20"
 modular-bitfield = "0.11.2"
 num = "0.4.1"

+ 15 - 0
README.md

@@ -83,6 +83,21 @@ Field: !Frequency 1000
 Field: !PulseWidth 2 # Increment
 ```
 
+Randomizing pen values:
+
+``` yaml
+Op:
+  - !RandomizePen
+    Index: 0
+    Count: 10
+    
+    # Specify one or more of the following:
+    Speed: [100, 1000, 100] # [min, max, incr]
+    Power: [10, 100, 5] # [min, max, incr]
+    Frequency: [20000, 100000, 1000] # [min, max, incr]
+    PulseWidth: [2, 250] #[min, max]
+```
+
 Exporting a pen to a file:
 
 ``` yaml

+ 49 - 47
src/config/mod.rs

@@ -1,47 +1,49 @@
-use ezcad::{array_of::ArrayOf, layer::Layer, pen::Pen};
-use serde::{Deserialize, Serialize};
-
-use self::{
-    object::{DeleteObjects, ObjectOperation},
-    pen::{ClonePen, ImportExportPen, PatchPen, PatternPen},
-};
-
-pub mod object;
-pub mod pen;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub enum Operation {
-    PatchPen(PatchPen),
-    ClonePen(ClonePen),
-    PatternPen(PatternPen),
-    ExportPen(ImportExportPen),
-    ImportPen(ImportExportPen),
-    DeleteObjects(DeleteObjects),
-    Object(ObjectOperation),
-}
-
-pub trait Operations {
-    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>);
-}
-
-impl Operations for Vec<Operation> {
-    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>) {
-        for op in self {
-            match op {
-                Operation::PatchPen(x) => x.patch(pens),
-                Operation::ClonePen(x) => x.clone(pens),
-                Operation::PatternPen(x) => x.pattern(pens),
-                Operation::ImportPen(x) => x.import(pens),
-                Operation::ExportPen(x) => x.export(pens),
-                Operation::DeleteObjects(x) => x.delete(layers),
-                Operation::Object(x) => x.process(pens, layers),
-            }
-        }
-    }
-}
-
-#[derive(Debug, Serialize, Deserialize)]
-#[serde(rename_all = "PascalCase")]
-pub struct Config {
-    pub ops: Vec<Operation>,
-}
+use ezcad::{array_of::ArrayOf, layer::Layer, pen::Pen};
+use serde::{Deserialize, Serialize};
+
+use self::{
+    object::{DeleteObjects, ObjectOperation},
+    pen::{ClonePen, ImportExportPen, PatchPen, PatternPen, RandomizePen},
+};
+
+pub mod object;
+pub mod pen;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum Operation {
+    PatchPen(PatchPen),
+    ClonePen(ClonePen),
+    PatternPen(PatternPen),
+    RandomizePen(RandomizePen),
+    ExportPen(ImportExportPen),
+    ImportPen(ImportExportPen),
+    DeleteObjects(DeleteObjects),
+    Object(ObjectOperation),
+}
+
+pub trait Operations {
+    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>);
+}
+
+impl Operations for Vec<Operation> {
+    fn apply(&self, pens: &mut Vec<Pen>, layers: &mut ArrayOf<Layer>) {
+        for op in self {
+            match op {
+                Operation::PatchPen(x) => x.patch(pens),
+                Operation::ClonePen(x) => x.clone(pens),
+                Operation::PatternPen(x) => x.pattern(pens),
+                Operation::RandomizePen(x) => x.random(pens),
+                Operation::ImportPen(x) => x.import(pens),
+                Operation::ExportPen(x) => x.export(pens),
+                Operation::DeleteObjects(x) => x.delete(layers),
+                Operation::Object(x) => x.process(pens, layers),
+            }
+        }
+    }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+pub struct Config {
+    pub ops: Vec<Operation>,
+}

+ 67 - 0
src/config/pen.rs

@@ -4,7 +4,9 @@ use ezcad::{
     pen::Pen,
     types::{PulseWidth, Rgba},
 };
+use itertools::Itertools;
 use log::debug;
+use rand::{seq::SliceRandom, Rng};
 use serde::{Deserialize, Serialize};
 use strum::IntoEnumIterator;
 
@@ -317,6 +319,71 @@ impl PatternPen {
     }
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+pub struct RandomizePen {
+    index: usize,
+    count: usize,
+    speed: Option<(f64, f64, f64)>,
+    power: Option<(f64, f64, f64)>,
+    frequency: Option<(u32, u32, u32)>,
+    pulse_width: Option<(PulseWidth, PulseWidth)>,
+}
+
+impl RandomizePen {
+    pub fn random(&self, pens: &mut Vec<Pen>) {
+        debug!(
+            "Randomizing from pen #{} to #{}",
+            self.index,
+            self.index + self.count - 1
+        );
+
+        for (index, pen) in pens
+            .iter_mut()
+            .skip(self.index)
+            .take(self.count)
+            .enumerate()
+        {
+            if let Some((min, max, incr)) = self.speed {
+                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / incr) as usize);
+                let value: f64 = min + incr * offset as f64;
+                debug!("Randomizing speed for pen #{} to {}", index, value);
+                *pen.speed = value;
+            }
+
+            if let Some((min, max, incr)) = self.power {
+                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / incr) as usize);
+                let value: f64 = min + incr * offset as f64;
+                debug!("Randomizing power for pen #{} to {}", index, value);
+                *pen.power = value;
+            }
+
+            if let Some((min, max, incr)) = self.frequency {
+                let offset: usize = rand::thread_rng().gen_range(0..=((max - min) / incr) as usize);
+                let value: u32 = min + incr * 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)) = self.pulse_width {
+                let mut pw = PulseWidth::iter();
+                let mut v: Vec<PulseWidth> = vec![pw.find(|x| *x == min).unwrap()];
+                v.extend(pw.take_while_inclusive(|x| *x != max).collect_vec());
+
+                let width: &PulseWidth = v
+                    .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();
+            }
+        }
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize)]
 #[serde(rename_all = "PascalCase")]
 pub struct ImportExportPen {