migrate Color -> ItemColor and remove unused

This commit is contained in:
🪞👃🪞 2024-11-02 14:54:02 +02:00
parent 33600e890f
commit eb1e3179a4
6 changed files with 59 additions and 76 deletions

View file

@ -2,33 +2,37 @@ use crate::*;
use rand::{thread_rng, distributions::uniform::UniformSampler};
pub use palette::{*, convert::*, okhsl::*};
#[derive(Debug, Copy, Clone)]
/// A color in OKHSL and RGB representations.
#[derive(Debug, Default, Copy, Clone)]
pub struct ItemColor {
pub okhsl: Okhsl<f32>,
pub rgb: Color,
}
#[derive(Debug, Copy, Clone)]
/// A color in OKHSL and RGB with lighter and darker variants.
#[derive(Debug, Default, Copy, Clone)]
pub struct ItemColorTriplet {
pub base: ItemColor,
pub light: ItemColor,
pub dark: ItemColor,
}
/// Adds TUI RGB representation to an OKHSL value.
impl From<Okhsl<f32>> for ItemColor {
fn from (okhsl: Okhsl<f32>) -> Self {
Self { okhsl, rgb: okhsl_to_rgb(okhsl) }
}
fn from (okhsl: Okhsl<f32>) -> Self { Self { okhsl, rgb: okhsl_to_rgb(okhsl) } }
}
/// Adds OKHSL representation to a TUI RGB value.
impl From<Color> for ItemColor {
fn from (rgb: Color) -> Self { Self { rgb, okhsl: rgb_to_okhsl(rgb) } }
}
impl ItemColor {
pub fn random () -> Self {
random_okhsl().into()
}
pub fn random () -> Self { random_okhsl().into() }
pub fn random_dark () -> Self {
random_okhsl_dark().into()
let mut rng = thread_rng();
let lo = Okhsl::new(-180.0, 0.01, 0.05);
let hi = Okhsl::new( 180.0, 0.5, 0.2);
UniformOkhsl::new(lo, hi).sample(&mut rng).into()
}
pub fn random_near (other: Self, distance: f32) -> Self {
if distance > 1.0 {
panic!("random_color_near requires distance between 0.0 and 1.0");
}
if distance > 1.0 { panic!("ItemColor::random_near takes distance between 0.0 and 1.0"); }
other.okhsl.mix(random_okhsl(), distance).into()
}
}
@ -46,18 +50,9 @@ impl From<ItemColor> for ItemColorTriplet {
pub fn random_okhsl () -> Okhsl<f32> {
let mut rng = thread_rng();
UniformOkhsl::new(
Okhsl::new(-180.0, 0.01, 0.2),
Okhsl::new( 180.0, 0.9, 0.5),
).sample(&mut rng)
}
pub fn random_okhsl_dark () -> Okhsl<f32> {
let mut rng = thread_rng();
UniformOkhsl::new(
Okhsl::new(-180.0, 0.01, 0.05),
Okhsl::new( 180.0, 0.5, 0.2),
).sample(&mut rng)
let lo = Okhsl::new(-180.0, 0.01, 0.2);
let hi = Okhsl::new( 180.0, 0.9, 0.5);
UniformOkhsl::new(lo, hi).sample(&mut rng)
}
pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
@ -65,26 +60,14 @@ pub fn okhsl_to_rgb (color: Okhsl<f32>) -> Color {
Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,)
}
pub fn random_color () -> Color {
okhsl_to_rgb(random_okhsl())
}
pub fn random_color_dark () -> Color {
okhsl_to_rgb(random_okhsl_dark())
}
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)
pub fn rgb_to_okhsl (color: Color) -> Okhsl<f32> {
if let Color::Rgb(r, g, b) = color {
Okhsl::from_color(Srgb::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
))
} 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");
unreachable!("only Color::Rgb is supported")
}
okhsl_to_rgb(Okhsl::from_color(Srgb::new(
r as f32 / 255.0,
g as f32 / 255.0,
b as f32 / 255.0,
)).mix(random_okhsl(), distance))
}

View file

@ -67,7 +67,7 @@ pub struct Arrangement<E: Engine> {
/// Whether the arranger is currently focused
pub focused: bool,
/// Background color of arrangement
pub color: Color,
pub color: ItemColor,
/// Width and height of arrangement area at last render
pub size: Measure<E>,
}
@ -78,7 +78,7 @@ pub struct ArrangementTrack {
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: Color,
pub color: ItemColor,
/// MIDI player/recorder
pub player: PhrasePlayer,
}
@ -89,7 +89,7 @@ pub struct Scene {
/// Clips in scene, one per track
pub clips: Vec<Option<Arc<RwLock<Phrase>>>>,
/// Identifying color of scene
pub color: Color,
pub color: ItemColor,
}
#[derive(PartialEq, Clone, Copy)]
/// Represents the current user selection in the arranger
@ -232,7 +232,7 @@ impl<E: Engine> Arrangement<E> {
scenes: vec![],
tracks: vec![],
focused: false,
color: Color::Rgb(28, 35, 25),
color: Color::Rgb(28, 35, 25).into(),
size: Measure::new(),
}
}
@ -374,11 +374,11 @@ impl<E: Engine> Arrangement<E> {
}
pub fn randomize_color (&mut self) {
match self.selected {
ArrangementFocus::Mix => { self.color = random_color_dark() },
ArrangementFocus::Track(t) => { self.tracks[t].color = random_color() },
ArrangementFocus::Scene(s) => { self.scenes[s].color = random_color() },
ArrangementFocus::Mix => { self.color = ItemColor::random_dark() },
ArrangementFocus::Track(t) => { self.tracks[t].color = ItemColor::random() },
ArrangementFocus::Scene(s) => { self.scenes[s].color = ItemColor::random() },
ArrangementFocus::Clip(t, s) => if let Some(phrase) = &self.scenes[s].clips[t] {
phrase.write().unwrap().color = random_color();
phrase.write().unwrap().color = ItemColor::random();
}
}
}
@ -396,7 +396,7 @@ impl<E: Engine> Arrangement<E> {
pub fn track_next (&mut self) { self.selected.track_next(self.tracks.len() - 1) }
pub fn track_prev (&mut self) { self.selected.track_prev() }
pub fn track_add (
&mut self, name: Option<&str>, color: Option<Color>
&mut self, name: Option<&str>, color: Option<ItemColor>
) -> Usually<&mut ArrangementTrack> {
self.tracks.push(name.map_or_else(
|| ArrangementTrack::new(&self.clock, &self.track_default_name(), color),
@ -442,7 +442,7 @@ impl<E: Engine> Arrangement<E> {
pub fn scene_prev (&mut self) {
self.selected.scene_prev()
}
pub fn scene_add (&mut self, name: Option<&str>, color: Option<Color>) -> Usually<&mut Scene> {
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemColor>) -> Usually<&mut Scene> {
let clips = vec![None;self.tracks.len()];
let name = name.map(|x|x.to_string()).unwrap_or_else(||self.scene_default_name());
self.scenes.push(Scene::new(name, clips, color));
@ -523,11 +523,11 @@ impl<E: Engine> Arrangement<E> {
}
}
impl ArrangementTrack {
pub fn new (clock: &Arc<TransportTime>, name: &str, color: Option<Color>) -> Self {
pub fn new (clock: &Arc<TransportTime>, name: &str, color: Option<ItemColor>) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
width: name.len() + 2,
color: color.unwrap_or_else(random_color),
color: color.unwrap_or_else(ItemColor::random),
player: PhrasePlayer::new(clock),
}
}
@ -640,12 +640,12 @@ impl Scene {
pub fn new (
name: impl AsRef<str>,
clips: impl AsRef<[Option<Arc<RwLock<Phrase>>>]>,
color: Option<Color>,
color: Option<ItemColor>,
) -> 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),
color: color.unwrap_or_else(ItemColor::random),
}
}
/// Returns the pulse length of the longest phrase in the scene

View file

@ -33,7 +33,7 @@ impl ArrangerCli {
for i in 0..self.tracks {
let _track = arrangement.track_add(
None,
Some(okhsl_to_rgb(track_color_1.mix(track_color_2, i as f32 / self.tracks as f32)))
Some(track_color_1.mix(track_color_2, i as f32 / self.tracks as f32).into())
)?;
}
let scene_color_1 = random_okhsl();
@ -41,7 +41,7 @@ impl ArrangerCli {
for i in 0..self.scenes {
let _scene = arrangement.scene_add(
None,
Some(okhsl_to_rgb(scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32)))
Some(scene_color_1.mix(scene_color_2, i as f32 / self.scenes as f32).into())
)?;
}
let arranger = Arc::new(RwLock::new(Arranger::new(

View file

@ -182,7 +182,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
.unwrap_or("(none)".into()));
col!(name, input)
.min_xy(w as u16, track_title_h)
.bg(track.color)
.bg(track.color.rgb)
.push_x(scene_title_w)
});
// track controls
@ -219,7 +219,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
.unwrap_or("(none)".into()));
col!(until_next, elapsed, output)
.min_xy(w as u16, tracks_footer)
.bg(track.color)
.bg(track.color.rgb)
.push_x(scene_title_w)
});
// scene titles
@ -236,7 +236,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let name = format!("{}", name);
let max_w = name.len().min((w as usize).saturating_sub(2));
add(&name.as_str()[0..max_w].push_x(1).fixed_x(w))?;
color = (phrase as &Arc<RwLock<Phrase>>).read().unwrap().color;
color = (phrase as &Arc<RwLock<Phrase>>).read().unwrap().color.rgb;
},
_ => {}
};
@ -250,7 +250,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let playing = scene.is_playing(tracks);
Stack::right(move |add| {
// scene title:
add(&scene_name(scene, playing, height).bg(scene.color))?;
add(&scene_name(scene, playing, height).bg(scene.color.rgb))?;
// clip per track:
Ok(for (track, w) in cols.iter().map(|col|col.0).enumerate() {
add(&scene_clip(scene, track, w as u16, height))?;
@ -308,7 +308,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
else { area.clip_w(scene_title_w).clip_h(track_title_h) }, &CORNERS)?
})
}))
}).bg(bg);//.grow_y(1);//.border(border);
}).bg(bg.rgb);//.grow_y(1);//.border(border);
let color = if self.0.focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
let size = format!("{}x{}", self.0.size.w(), self.0.size.h());
let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();

View file

@ -79,7 +79,7 @@ pub enum PhrasePoolMode {
/// All notes are displayed with minimum length
pub percussive: bool,
/// Identifying color of phrase
pub color: Color,
pub color: ItemColor,
}
/// Contains state for viewing and editing a phrase
pub struct PhraseEditor<E: Engine> {
@ -205,7 +205,7 @@ impl<E: Engine> PhrasePool<E> {
}
return None
}
fn new_phrase (name: Option<&str>, color: Option<Color>) -> Arc<RwLock<Phrase>> {
fn new_phrase (name: Option<&str>, color: Option<ItemColor>) -> Arc<RwLock<Phrase>> {
Arc::new(RwLock::new(Phrase::new(
String::from(name.unwrap_or("(new)")), true, 4 * PPQ, None, color
)))
@ -216,23 +216,23 @@ impl<E: Engine> PhrasePool<E> {
self.phrase = self.phrase.min(self.phrases.len().saturating_sub(1));
}
}
pub fn append_new (&mut self, name: Option<&str>, color: Option<Color>) {
pub fn append_new (&mut self, name: Option<&str>, color: Option<ItemColor>) {
self.phrases.push(Self::new_phrase(name, color));
self.phrase = self.phrases.len() - 1;
}
pub fn insert_new (&mut self, name: Option<&str>, color: Option<Color>) {
pub fn insert_new (&mut self, name: Option<&str>, color: Option<ItemColor>) {
self.phrases.insert(self.phrase + 1, Self::new_phrase(name, color));
self.phrase += 1;
}
pub fn insert_dup (&mut self) {
let mut phrase = self.phrases[self.phrase].read().unwrap().duplicate();
phrase.color = random_color_near(phrase.color, 0.25);
phrase.color = ItemColor::random_near(phrase.color, 0.25);
self.phrases.insert(self.phrase + 1, Arc::new(RwLock::new(phrase)));
self.phrase += 1;
}
pub fn randomize_color (&mut self) {
let mut phrase = self.phrases[self.phrase].write().unwrap();
phrase.color = random_color();
phrase.color = ItemColor::random();
}
pub fn begin_rename (&mut self) {
self.mode = Some(PhrasePoolMode::Rename(
@ -347,7 +347,7 @@ impl Phrase {
loop_on: bool,
length: usize,
notes: Option<PhraseData>,
color: Option<Color>,
color: Option<ItemColor>,
) -> Self {
Self {
uuid: uuid::Uuid::new_v4(),
@ -359,7 +359,7 @@ impl Phrase {
loop_start: 0,
loop_length: length,
percussive: true,
color: color.unwrap_or_else(random_color)
color: color.unwrap_or_else(ItemColor::random)
}
}
pub fn duplicate (&self) -> Self {
@ -384,7 +384,7 @@ impl Phrase {
}
}
impl Default for Phrase {
fn default () -> Self { Self::new("(empty)", false, 0, None, Some(Color::Rgb(0, 0, 0))) }
fn default () -> Self { Self::new("(empty)", false, 0, None, Some(Color::Rgb(0, 0, 0).into())) }
}
impl PartialEq for Phrase { fn eq (&self, other: &Self) -> bool { self.uuid == other.uuid } }
impl Eq for Phrase {}

View file

@ -33,7 +33,7 @@ impl Content for PhrasePool<Tui> {
};
let row2 = TuiStyle::bold(row2, true);
let bg = if i == self.phrase { color } else { color };
add(&col!(row1, row2).fill_x().bg(bg))?;
add(&col!(row1, row2).fill_x().bg(bg.rgb))?;
Ok(if *focused && i == self.phrase { add(&CORNERS)?; })
})
);
@ -57,7 +57,7 @@ impl Content for PhraseEditor<Tui> {
start: time_start, point: time_point, clamp: time_clamp, scale: time_scale
} = *self.time_axis.read().unwrap();
let color = Color::Rgb(0,255,0);
let color = phrase.as_ref().map(|p|p.read().unwrap().color).unwrap_or(color);
let color = phrase.as_ref().map(|p|p.read().unwrap().color.rgb).unwrap_or(color);
let keys = CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(5))), move|to: &mut TuiOutput|{
Ok(if to.area().h() >= 2 {
to.buffer_update(to.area().set_w(5), &|cell, x, y|{