From 6bee5b0bcd1f973e5309c2d66683fa79d487dba2 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 11 Oct 2024 18:02:03 +0300 Subject: [PATCH] scene and track colors; random_color_near --- crates/tek_sequencer/src/arranger.rs | 23 ++++++++---- crates/tek_sequencer/src/arranger_tui.rs | 10 +++--- crates/tek_sequencer/src/lib.rs | 44 +++++++++++++++++++++++ crates/tek_sequencer/src/sequencer.rs | 38 +++++++------------- crates/tek_sequencer/src/sequencer_tui.rs | 3 +- 5 files changed, 80 insertions(+), 38 deletions(-) diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 71b1bb62..c2ef46da 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -47,11 +47,17 @@ pub struct ArrangementTrack { pub outputs: Vec<()>, /// Preferred width of track column pub width: usize, + /// Identifying color of track + pub color: Color, } #[derive(Default)] pub struct Scene { + /// Name of scene pub name: Arc>, + /// Clips in scene, one per track pub clips: Vec>>>, + /// Identifying color of scene + pub color: Color, } #[derive(PartialEq, Clone, Copy)] /// Represents the current user selection in the arranger @@ -199,8 +205,8 @@ impl Arrangement { } pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut ArrangementTrack> { self.tracks.push(name.map_or_else( - || ArrangementTrack::new(&self.track_default_name()), - |name| ArrangementTrack::new(name), + || ArrangementTrack::new(&self.track_default_name(), None), + |name| ArrangementTrack::new(name, None), )); let index = self.tracks.len() - 1; Ok(&mut self.tracks[index]) @@ -240,8 +246,8 @@ impl Arrangement { pub fn scene_add (&mut self, name: Option<&str>) -> Usually<&mut Scene> { let clips = vec![None;self.tracks.len()]; self.scenes.push(match name { - Some(name) => Scene::new(name, clips), - None => Scene::new(&self.scene_default_name(), clips), + Some(name) => Scene::new(name, clips, None), + None => Scene::new(&self.scene_default_name(), clips, None), }); let index = self.scenes.len() - 1; Ok(&mut self.scenes[index]) @@ -344,13 +350,14 @@ impl Arrangement { } } impl ArrangementTrack { - pub fn new (name: &str) -> Self { + pub fn new (name: &str, color: Option) -> Self { Self { name: Arc::new(RwLock::new(name.into())), inputs: vec![], - player: PhrasePlayer::new(name), + player: PhrasePlayer::new(), outputs: vec![], width: name.len() + 2, + color: color.unwrap_or_else(random_color) } } pub fn longest_name (tracks: &[Self]) -> usize { @@ -491,11 +498,13 @@ impl ArrangementViewMode { impl Scene { pub fn new ( name: impl AsRef, - clips: impl AsRef<[Option>>]> + clips: impl AsRef<[Option>>]>, + color: Option, ) -> Self { Self { name: Arc::new(RwLock::new(name.as_ref().into())), clips: clips.as_ref().iter().map(|x|x.clone()).collect(), + color: color.unwrap_or_else(random_color), } } /// Returns the pulse length of the longest phrase in the scene diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index dfdc68dd..5852cbef 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -169,14 +169,16 @@ impl<'a> Content for VerticalArranger<'a, Tui> { //let height = rows.last().map(|(w,y)|(y+w)/PPQ).unwrap_or(16); let tracks: &[ArrangementTrack] = state.tracks.as_ref(); let scenes: &[Scene] = state.scenes.as_ref(); - let offset = 4 + Scene::longest_name(scenes) as u16; + let offset = 3 + Scene::longest_name(scenes) as u16; let content = Layers::new(move |add|{ let rows: &[(usize, usize)] = rows.as_ref(); let cols: &[(usize, usize)] = cols.as_ref(); let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) => (&track.name.read().unwrap().as_str() as &dyn Widget) - .min_xy(*w as u16, 2).push_x(offset)); + .min_xy(*w as u16, 2) + .bg(track.color) + .push_x(offset)); let scene_name = |scene, playing: bool, height|row!( if playing { "▶ " } else { " " }, @@ -186,7 +188,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> { let scene_clip = |scene, track: usize, w: u16, h: u16|Layers::new(move |add|{ let mut color = Color::Rgb(40, 50, 30); match (tracks.get(track), (scene as &Scene).clips.get(track)) { - (Some(track), Some(Some(phrase))) => { + (Some(_), Some(Some(phrase))) => { let name = &(phrase as &Arc>).read().unwrap().name; let name = format!("{}", name); add(&name.as_str().push_x(1).fixed_x(w))?; @@ -206,7 +208,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> { let height = 1.max((pulses / PPQ) as u16); let playing = scene.is_playing(tracks); Stack::right(move |add| { - add(&scene_name(scene, playing, height))?; + add(&scene_name(scene, playing, height).bg(scene.color))?; for (track, (w, _)) in cols.iter().enumerate() { add(&scene_clip(scene, track, *w as u16, height))?; } diff --git a/crates/tek_sequencer/src/lib.rs b/crates/tek_sequencer/src/lib.rs index 5ead3524..9bda7626 100644 --- a/crates/tek_sequencer/src/lib.rs +++ b/crates/tek_sequencer/src/lib.rs @@ -34,3 +34,47 @@ tui_style!(STYLE_LABEL = Some(Color::Reset), None, None, Modifier::empty(), Modifier::BOLD); tui_style!(STYLE_VALUE = Some(Color::White), None, None, Modifier::BOLD, Modifier::DIM); + +pub fn random_color () -> Color { + let mut rng = thread_rng(); + let color: Okhsl = Okhsl::new( + rng.gen::() * 360f32 - 180f32, + rng.gen::(), + rng.gen::() * 0.5, + ); + let color: Srgb = Srgb::from_color_unclamped(color); + Color::Rgb( + (color.red * 255.0) as u8, + (color.green * 255.0) as u8, + (color.blue * 255.0) as u8, + ) +} + +pub fn random_color_near (color: Color, distance: f32) -> Color { + let (r, g, b) = if let Color::Rgb(r, g, b) = color { + (r, g, b) + } else { + panic!("random_color_near works only with Color::Rgb") + }; + if distance > 1.0 { + panic!("random_color_near requires distance between 0.0 and 1.0"); + } + let old: Okhsl = Okhsl::from([ + r as f32 / 255.0, + g as f32 / 255.0, + b as f32 / 255.0, + ]); + let mut rng = thread_rng(); + let new: Okhsl = Okhsl::new( + rng.gen::() * 360f32 - 180f32, + rng.gen::(), + rng.gen::() * 0.66, + ); + let mixed = new.mix(old, 0.5); + let color: Srgb = Srgb::from_color_unclamped(mixed); + Color::Rgb( + (color.red * 255.0) as u8, + (color.green * 255.0) as u8, + (color.blue * 255.0) as u8, + ) +} diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index f4d5ecbd..f3c371b9 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -158,20 +158,6 @@ impl PhraseEditor { } } } -pub fn random_color () -> Color { - let mut rng = thread_rng(); - let color: Okhsl = Okhsl::new( - rng.gen::() * 360f32 - 180f32, - rng.gen::() * 0.5 + 0.4, - rng.gen::() * 0.5 + 0.15, - ); - let color: Srgb = Srgb::from_color_unclamped(color); - Color::Rgb( - (color.red * 255.0) as u8, - (color.green * 255.0) as u8, - (color.blue * 255.0) as u8, - ) -} impl Phrase { pub fn new ( name: impl AsRef, @@ -257,19 +243,19 @@ impl std::cmp::PartialEq for Phrase { } impl Eq for Phrase {} impl PhrasePlayer { - pub fn new (name: &str) -> Self { + pub fn new () -> Self { Self { - _engine: Default::default(), - phrase: None, - notes_in: Arc::new(RwLock::new([false;128])), - notes_out: Arc::new(RwLock::new([false;128])), - monitoring: false, - recording: false, - overdub: true, - midi_out: None, - midi_out_buf: vec![Vec::with_capacity(16);16384], - reset: true, - now: 0, + _engine: Default::default(), + phrase: None, + notes_in: Arc::new(RwLock::new([false;128])), + notes_out: Arc::new(RwLock::new([false;128])), + monitoring: false, + recording: false, + overdub: true, + midi_out: None, + midi_out_buf: vec![Vec::with_capacity(16);16384], + reset: true, + now: 0, } } pub fn toggle_monitor (&mut self) { diff --git a/crates/tek_sequencer/src/sequencer_tui.rs b/crates/tek_sequencer/src/sequencer_tui.rs index 5fd9719a..339db77e 100644 --- a/crates/tek_sequencer/src/sequencer_tui.rs +++ b/crates/tek_sequencer/src/sequencer_tui.rs @@ -120,7 +120,8 @@ impl Handle for PhrasePool { self.phrase += 1; }, key!(KeyCode::Char('d')) => { // insert duplicate - let phrase = (*self.phrases[self.phrase].read().unwrap()).clone(); + let mut phrase = (*self.phrases[self.phrase].read().unwrap()).clone(); + phrase.color = random_color_near(phrase.color, 0.1); self.phrases.insert(self.phrase + 1, Arc::new(RwLock::new(phrase))); self.phrase += 1; },