|
@@ -10,6 +10,8 @@ use rand::{seq::SliceRandom, Rng};
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
use strum::IntoEnumIterator;
|
|
|
|
|
|
+use super::double_window_mut;
|
|
|
+
|
|
|
const SPEED_MIN: f64 = 0.0;
|
|
|
const SPEED_MAX: f64 = 100000.0;
|
|
|
const POWER_MIN: f64 = 0.0;
|
|
@@ -106,7 +108,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();
|
|
|
+ pen.valid_settings(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -169,7 +171,7 @@ impl ClonePen {
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
-pub enum PatternField {
|
|
|
+pub enum PatternPenField {
|
|
|
Loops(i32),
|
|
|
Speed(f64),
|
|
|
Power(f64),
|
|
@@ -177,77 +179,48 @@ pub enum PatternField {
|
|
|
PulseWidth(u32),
|
|
|
}
|
|
|
|
|
|
-impl PatternField {
|
|
|
+impl PatternPenField {
|
|
|
pub fn pattern(&self, pens: &mut dyn Iterator<Item = (usize, &mut Pen)>) {
|
|
|
- // Obtain settings from source (first) pen
|
|
|
- let (src_idx, src) = pens.next().expect("Pattern must involve at least one pen");
|
|
|
-
|
|
|
- let mut setting: PatternField = match self {
|
|
|
- PatternField::Loops(_) => {
|
|
|
- debug!(
|
|
|
- "Initial loop count from pen #{} is {}",
|
|
|
- src_idx, *src.loop_count
|
|
|
- );
|
|
|
- PatternField::Loops((*src.loop_count).try_into().unwrap())
|
|
|
- }
|
|
|
- PatternField::Speed(_) => {
|
|
|
- debug!("Initial speed from pen #{} is {}", src_idx, *src.speed);
|
|
|
- PatternField::Speed(*src.speed)
|
|
|
- }
|
|
|
- PatternField::Power(_) => {
|
|
|
- debug!("Initial power from pen #{} is {}", src_idx, *src.power);
|
|
|
- PatternField::Power(*src.power)
|
|
|
- }
|
|
|
- PatternField::Frequency(_) => {
|
|
|
- debug!(
|
|
|
- "Initial frequency from pen #{} is {}",
|
|
|
- src_idx, *src.frequency
|
|
|
- );
|
|
|
- PatternField::Frequency((*src.frequency).try_into().unwrap())
|
|
|
- }
|
|
|
- PatternField::PulseWidth(_) => {
|
|
|
- debug!(
|
|
|
- "Initial pulse width from pen #{} is {}ns",
|
|
|
- src_idx, *src.pulse_width
|
|
|
- );
|
|
|
- PatternField::PulseWidth(*src.pulse_width)
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- for (idx, dst) in pens {
|
|
|
- // Calculate new setting
|
|
|
- setting = match (setting, self) {
|
|
|
- (PatternField::Loops(prev), PatternField::Loops(incr)) => {
|
|
|
- let value: i32 = prev + incr;
|
|
|
- debug!("Patching loop count for pen #{} to {}", idx, value);
|
|
|
+ let mut pens: Vec<(usize, &mut Pen)> = pens.collect_vec();
|
|
|
+
|
|
|
+ double_window_mut(&mut pens[..], |prev, next| {
|
|
|
+ let _prev_idx: usize = prev.0;
|
|
|
+ let prev: &mut Pen = prev.1;
|
|
|
+ let next_idx: usize = next.0;
|
|
|
+ let next: &mut Pen = next.1;
|
|
|
+
|
|
|
+ match self {
|
|
|
+ PatternPenField::Loops(incr) => {
|
|
|
+ let value: i32 = i32::try_from(*prev.loop_count).unwrap() + incr;
|
|
|
+ debug!("Patching loop count for pen #{} to {}", next_idx, value);
|
|
|
assert!(value > 0, "Pen loop count must be greater than zero");
|
|
|
- PatternField::Loops(value)
|
|
|
+ *next.loop_count = value.try_into().unwrap()
|
|
|
}
|
|
|
- (PatternField::Speed(prev), PatternField::Speed(incr)) => {
|
|
|
- let value: f64 = prev + incr;
|
|
|
- debug!("Patching speed for pen #{} to {}", idx, value);
|
|
|
+ PatternPenField::Speed(incr) => {
|
|
|
+ let value: f64 = *prev.speed + incr;
|
|
|
+ debug!("Patching speed for pen #{} to {}", next_idx, value);
|
|
|
assert!(
|
|
|
value > SPEED_MIN && value <= SPEED_MAX,
|
|
|
"Pen speed must be between {} and {}",
|
|
|
SPEED_MIN,
|
|
|
SPEED_MAX
|
|
|
);
|
|
|
- PatternField::Speed(value)
|
|
|
+ *next.speed = value;
|
|
|
}
|
|
|
- (PatternField::Power(prev), PatternField::Power(incr)) => {
|
|
|
- let value: f64 = prev + incr;
|
|
|
- debug!("Patching power for pen #{} to {}", idx, value);
|
|
|
+ PatternPenField::Power(incr) => {
|
|
|
+ let value: f64 = *prev.power + incr;
|
|
|
+ debug!("Patching power for pen #{} to {}", next_idx, value);
|
|
|
assert!(
|
|
|
value > POWER_MIN && value <= POWER_MAX,
|
|
|
"Pen power must be between {} and {}",
|
|
|
POWER_MIN,
|
|
|
POWER_MAX
|
|
|
);
|
|
|
- PatternField::Power(value)
|
|
|
+ *next.power = value;
|
|
|
}
|
|
|
- (PatternField::Frequency(prev), PatternField::Frequency(incr)) => {
|
|
|
- let value: i32 = prev + incr;
|
|
|
- debug!("Patching frequency for pen #{} to {}", idx, value);
|
|
|
+ PatternPenField::Frequency(incr) => {
|
|
|
+ let value: i32 = i32::try_from(*prev.frequency).unwrap() + incr;
|
|
|
+ debug!("Patching frequency for pen #{} to {}", next_idx, value);
|
|
|
assert!(
|
|
|
value >= FREQUENCY_MIN.try_into().unwrap()
|
|
|
&& value <= FREQUENCY_MAX.try_into().unwrap(),
|
|
@@ -255,45 +228,31 @@ impl PatternField {
|
|
|
FREQUENCY_MIN,
|
|
|
FREQUENCY_MAX
|
|
|
);
|
|
|
- PatternField::Frequency(value)
|
|
|
+ *next.frequency = value.try_into().unwrap();
|
|
|
+ *next.frequency_2 = value.try_into().unwrap();
|
|
|
}
|
|
|
- (PatternField::PulseWidth(prev), PatternField::PulseWidth(incr)) => {
|
|
|
+ PatternPenField::PulseWidth(incr) => {
|
|
|
let mut pw = PulseWidth::iter();
|
|
|
let _ = pw
|
|
|
- .find(|x| u32::from(*x) == prev)
|
|
|
+ .find(|x| u32::from(*x) == *prev.pulse_width)
|
|
|
.expect("Unknown pulse width");
|
|
|
|
|
|
let mut pw = pw.skip((*incr - 1).try_into().unwrap());
|
|
|
- let next: u32 = pw.next().expect("Pulse width out of bounds").into();
|
|
|
- debug!("Patching pulse width for pen #{} to {}ns", idx, next);
|
|
|
- PatternField::PulseWidth(next)
|
|
|
- }
|
|
|
- _ => unreachable!(),
|
|
|
- };
|
|
|
-
|
|
|
- // Patch updated value
|
|
|
- match setting {
|
|
|
- PatternField::Loops(x) => *dst.loop_count = x.try_into().unwrap(),
|
|
|
- PatternField::Speed(x) => *dst.speed = x,
|
|
|
- PatternField::Power(x) => *dst.power = x,
|
|
|
- PatternField::Frequency(x) => {
|
|
|
- *dst.frequency = x.try_into().unwrap();
|
|
|
- *dst.frequency_2 = x.try_into().unwrap();
|
|
|
- }
|
|
|
- PatternField::PulseWidth(x) => {
|
|
|
- *dst.pulse_width = x;
|
|
|
- *dst.pulse_width_2 = x.try_into().unwrap();
|
|
|
+ let pw: u32 = pw.next().expect("Pulse width out of bounds").into();
|
|
|
+ debug!("Patching pulse width for pen #{} to {}ns", next_idx, next);
|
|
|
+ *next.pulse_width = pw;
|
|
|
+ *next.pulse_width_2 = pw.try_into().unwrap();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Randomize pen color
|
|
|
- *dst.color = Rgba::random().into();
|
|
|
+ *next.color = Rgba::random().into();
|
|
|
|
|
|
// Always enable custom settings for pen
|
|
|
- *dst.use_default = 0;
|
|
|
+ *next.use_default = 0;
|
|
|
|
|
|
- dst.valid_settings();
|
|
|
- }
|
|
|
+ next.valid_settings(true);
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -302,7 +261,7 @@ impl PatternField {
|
|
|
pub struct PatternPen {
|
|
|
index: usize,
|
|
|
count: usize,
|
|
|
- field: PatternField,
|
|
|
+ field: PatternPenField,
|
|
|
}
|
|
|
|
|
|
impl PatternPen {
|
|
@@ -355,6 +314,8 @@ pub struct RandomizePen {
|
|
|
power: Option<(f64, f64, f64)>,
|
|
|
frequency: Option<(u32, u32, u32)>,
|
|
|
pulse_width: Option<(PulseWidth, PulseWidth, usize)>,
|
|
|
+ power_density: Option<(u64, u64)>,
|
|
|
+ enforce_limits: Option<bool>,
|
|
|
}
|
|
|
|
|
|
impl RandomizePen {
|
|
@@ -420,17 +381,32 @@ impl RandomizePen {
|
|
|
setting.pulse_width = Some(*width);
|
|
|
}
|
|
|
|
|
|
+ setting.apply(pen);
|
|
|
+
|
|
|
+ // Check if power density value is within range
|
|
|
+ if let Some((range_min, range_max)) = self.power_density {
|
|
|
+ let power_density: u64 = pen.power_density();
|
|
|
+ if power_density < range_min || power_density > range_max {
|
|
|
+ debug!("Retrying (power density {power_density} out of range)");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check settings
|
|
|
+ if self.enforce_limits.unwrap_or(true) {
|
|
|
+ debug!("Checking settings");
|
|
|
+ if !pen.valid_settings(false) {
|
|
|
+ debug!("Retrying (invalid setting)");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// Check for duplicates
|
|
|
if !generated.contains(&setting) {
|
|
|
generated.push(setting);
|
|
|
- setting.apply(pen);
|
|
|
- if !pen.valid_settings() {
|
|
|
- debug!("Retrying..");
|
|
|
- } else {
|
|
|
- break;
|
|
|
- }
|
|
|
+ break;
|
|
|
} else {
|
|
|
- debug!("Duplicate random setting");
|
|
|
+ debug!("Retrying (duplicate setting)");
|
|
|
}
|
|
|
|
|
|
// Fail out if max attempts reached (insufficient search space)
|
|
@@ -442,7 +418,7 @@ impl RandomizePen {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pen.valid_settings();
|
|
|
+ pen.valid_settings(true);
|
|
|
}
|
|
|
}
|
|
|
}
|