Euclidean + hue rotation
This commit is contained in:
88
src/app.rs
88
src/app.rs
@@ -111,6 +111,7 @@ impl App {
|
||||
flash_brightness: self.ui.flash_brightness,
|
||||
color_scheme: self.ui.color_scheme,
|
||||
layout: self.audio.config.layout,
|
||||
hue_rotation: self.ui.hue_rotation,
|
||||
..Default::default()
|
||||
},
|
||||
link: crate::settings::LinkSettings {
|
||||
@@ -1311,7 +1312,15 @@ impl App {
|
||||
}
|
||||
AppCommand::SetColorScheme(scheme) => {
|
||||
self.ui.color_scheme = scheme;
|
||||
crate::theme::set(scheme.to_theme());
|
||||
let base_theme = scheme.to_theme();
|
||||
let rotated = cagire_ratatui::theme::transform::rotate_theme(base_theme, self.ui.hue_rotation);
|
||||
crate::theme::set(rotated);
|
||||
}
|
||||
AppCommand::SetHueRotation(degrees) => {
|
||||
self.ui.hue_rotation = degrees;
|
||||
let base_theme = self.ui.color_scheme.to_theme();
|
||||
let rotated = cagire_ratatui::theme::transform::rotate_theme(base_theme, degrees);
|
||||
crate::theme::set(rotated);
|
||||
}
|
||||
AppCommand::ToggleRuntimeHighlight => {
|
||||
self.ui.runtime_highlight = !self.ui.runtime_highlight;
|
||||
@@ -1429,6 +1438,65 @@ impl App {
|
||||
AppCommand::ResetPeakVoices => {
|
||||
self.metrics.peak_voices = 0;
|
||||
}
|
||||
|
||||
// Euclidean distribution
|
||||
AppCommand::ApplyEuclideanDistribution {
|
||||
bank,
|
||||
pattern,
|
||||
source_step,
|
||||
pulses,
|
||||
steps,
|
||||
rotation,
|
||||
} => {
|
||||
let pat_len = self.project_state.project.pattern_at(bank, pattern).length;
|
||||
let rhythm = euclidean_rhythm(pulses, steps, rotation);
|
||||
|
||||
let mut created_count = 0;
|
||||
for (i, &is_hit) in rhythm.iter().enumerate() {
|
||||
if !is_hit {
|
||||
continue;
|
||||
}
|
||||
|
||||
let target = (source_step + i) % pat_len;
|
||||
|
||||
if target == source_step {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(step) = self
|
||||
.project_state
|
||||
.project
|
||||
.pattern_at_mut(bank, pattern)
|
||||
.step_mut(target)
|
||||
{
|
||||
step.source = Some(source_step);
|
||||
step.script.clear();
|
||||
step.command = None;
|
||||
step.active = true;
|
||||
}
|
||||
created_count += 1;
|
||||
}
|
||||
|
||||
self.project_state.mark_dirty(bank, pattern);
|
||||
|
||||
for (i, &is_hit) in rhythm.iter().enumerate() {
|
||||
if !is_hit || i == 0 {
|
||||
continue;
|
||||
}
|
||||
let target = (source_step + i) % pat_len;
|
||||
let saved = self.editor_ctx.step;
|
||||
self.editor_ctx.step = target;
|
||||
self.compile_current_step(link);
|
||||
self.editor_ctx.step = saved;
|
||||
}
|
||||
|
||||
self.load_step_to_editor();
|
||||
self.ui.flash(
|
||||
&format!("Created {} linked steps (E({pulses},{steps}))", created_count),
|
||||
200,
|
||||
FlashKind::Success,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,3 +1549,21 @@ impl App {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn euclidean_rhythm(pulses: usize, steps: usize, rotation: usize) -> Vec<bool> {
|
||||
if pulses == 0 || steps == 0 || pulses > steps {
|
||||
return vec![false; steps];
|
||||
}
|
||||
|
||||
let mut pattern = vec![false; steps];
|
||||
for i in 0..pulses {
|
||||
let pos = (i * steps) / pulses;
|
||||
pattern[pos] = true;
|
||||
}
|
||||
|
||||
if rotation > 0 {
|
||||
pattern.rotate_left(rotation % steps);
|
||||
}
|
||||
|
||||
pattern
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user