mid refactor, saving my place

main
Elijah Voigt 2 weeks ago
parent 97aa5cb20d
commit ee8a70cc16

@ -1,7 +1,10 @@
use bevy::{ use bevy::{
feathers::{containers::*, display::*, theme::ThemeProps}, feathers::{containers::*, theme::ThemeProps},
ui::{Checked, InteractionDisabled}, ui::{Checked, InteractionDisabled},
ui_widgets::{ActivateOnPress, ValueChange, checkbox_self_update, SliderStep, SliderPrecision, slider_self_update}, ui_widgets::{
ActivateOnPress, SliderPrecision, SliderStep, ValueChange, checkbox_self_update,
slider_self_update,
},
}; };
use engine::*; use engine::*;
@ -57,6 +60,9 @@ fn main() {
// TODO Visualize simulation run/pause buttons enable/disable // TODO Visualize simulation run/pause buttons enable/disable
toggle_interactive::<AudioPlayback>, toggle_interactive::<AudioPlayback>,
update_pitch_map_resource_ui.run_if(resource_changed::<PitchMap>), update_pitch_map_resource_ui.run_if(resource_changed::<PitchMap>),
reassign_cell_pitch.run_if(resource_changed::<PitchMap>),
update_audio,
update_material,
), ),
) )
.add_systems(Update, cell_lifecycle.run_if(on_message::<Lifecycle>)) .add_systems(Update, cell_lifecycle.run_if(on_message::<Lifecycle>))
@ -187,6 +193,10 @@ fn the_ui() -> impl Scene {
Transform Transform
pane() pane()
Children [ Children [
pane_header()
Children [
Text("Conways Game of Life With Sound") ThemedText,
],
pane_body() pane_body()
Children [ Children [
subpane() subpane()
@ -399,43 +409,24 @@ impl Coordinates {
(Vec2::new(self.x as f32, self.y as f32) * SCALE).extend(0.0) (Vec2::new(self.x as f32, self.y as f32) * SCALE).extend(0.0)
} }
fn as_color(&self) -> Color { /// For the purposes of creating assests, what does the given coordinate translate to CellPitch wise
// hue (0..360.0) evenly spaced x in -2..4 fn to_cell_pitch(&self) -> CellPitch {
// chroma (0..1) evenly spaced y in -2..5 let note = match self.x {
let hue = (360.0 / 5.0) * ((self.x + 2) % 5) as f32; -6 => Note::A,
let chroma = (1.0 / 5.0) * ((self.y + 2) % 5) as f32; -5 => Note::ASharp,
let lightness = (1.0 / 5.0) * ((self.y + 2) % 5) as f32; -4 => Note::B,
Color::Oklcha(Oklcha { -3 => Note::C,
hue, -2 => Note::CSharp,
chroma, -1 => Note::D,
lightness, 0 => Note::DSharp,
..default() 1 => Note::E,
}) 2 => Note::F,
} 3 => Note::FSharp,
4 => Note::G,
fn as_cell_pitch(&self) -> CellPitch { 5 => Note::GSharp,
debug!("Generating cell pitch for {:?}", self);
let note = match (self.x + 2).abs() % 5 {
0 => Note::A,
1 => Note::B,
2 => Note::CSharp,
3 => Note::E,
4 => Note::FSharp,
_ => panic!("This shouldn't happen!"), _ => panic!("This shouldn't happen!"),
}; };
let octave = self.y;
let octave = ((self.y + 2).abs() % 5) - 2;
debug_assert!((-2..=2).contains(&octave));
debug!(
"\t{:?}",
CellPitch {
note: note.clone(),
octave
}
);
CellPitch { note, octave } CellPitch { note, octave }
} }
} }
@ -619,9 +610,9 @@ fn new_cell(
Coordinates { x, y } Coordinates { x, y }
template_value(playback_settings) template_value(playback_settings)
template_value(Transform::from_translation(c.as_translation() + Vec3::new(0.0, 0.0, 1.0))) template_value(Transform::from_translation(c.as_translation() + Vec3::new(0.0, 0.0, 1.0)))
template_value(AudioPlayerTemplate(cell_assets.pitches.get(&c.as_cell_pitch()).unwrap().clone().into()))
template_value(Mesh2dTemplate(cell_assets.mesh.clone().into())) template_value(Mesh2dTemplate(cell_assets.mesh.clone().into()))
template_value(MeshMaterial2dTemplate(cell_assets.materials.get(&c.as_cell_pitch()).unwrap().clone().into())) // TODO: template_value(AudioPlayerTemplate(cell_assets.pitches.get(&pitch_map.coordinates_cell_pitch(c)).unwrap().clone().into()))
// TODO: template_value(MeshMaterial2dTemplate(cell_assets.materials.get(&pitch_map.coordinates_cell_pitch(c)).unwrap().clone().into()))
} }
} }
@ -728,7 +719,7 @@ impl Note {
} }
} }
#[derive(Hash, PartialEq, Eq, Debug)] #[derive(Hash, PartialEq, Eq, Debug, Component)]
struct CellPitch { struct CellPitch {
note: Note, note: Note,
octave: isize, octave: isize,
@ -739,17 +730,59 @@ impl CellPitch {
// Thanks Claude Opus 4.8 // Thanks Claude Opus 4.8
self.note.as_frequency() * 2f32.powi(self.octave as i32) self.note.as_frequency() * 2f32.powi(self.octave as i32)
} }
fn to_color(&self) -> Color {
let hue = {
let segment = 360.0 / 12.0;
match self.note {
// Equally distribute hue among the OkLab spectrum (0..360)
Note::A => segment * 1.0,
Note::ASharp => segment * 2.0,
Note::B => segment * 3.0,
Note::C => segment * 4.0,
Note::CSharp => segment * 5.0,
Note::D => segment * 6.0,
Note::DSharp => segment * 7.0,
Note::E => segment * 8.0,
Note::F => segment * 9.0,
Note::FSharp => segment * 10.0,
Note::G => segment * 11.0,
Note::GSharp => segment * 12.0,
}
};
let lightness = (self.octave as f32 + 5.0) / 11.0;
Color::Oklcha(Oklcha {
hue,
lightness,
..default()
})
}
} }
fn load_audio_tones(mut pitches: ResMut<Assets<Pitch>>, mut cell_assets: ResMut<CellAssets>) { fn load_audio_tones(mut pitches: ResMut<Assets<Pitch>>, mut cell_assets: ResMut<CellAssets>) {
let notes = [Note::A, Note::B, Note::CSharp, Note::E, Note::FSharp]; let notes = [
let octaves = [-2, -1, 0, 1, 2]; Note::A,
Note::ASharp,
notes.iter().for_each(|note| { Note::B,
octaves.iter().for_each(|octave| { Note::C,
Note::CSharp,
Note::D,
Note::DSharp,
Note::E,
Note::F,
Note::FSharp,
Note::G,
Note::GSharp,
];
let octaves = -5..6;
octaves.for_each(|octave| {
notes.iter().for_each(|note| {
let cell_pitch = CellPitch { let cell_pitch = CellPitch {
note: note.clone(), note: note.clone(),
octave: *octave, octave,
}; };
let frequency = cell_pitch.to_frequency(); let frequency = cell_pitch.to_frequency();
let duration = Duration::from_secs(100); let duration = Duration::from_secs(100);
@ -764,7 +797,7 @@ fn load_audio_tones(mut pitches: ResMut<Assets<Pitch>>, mut cell_assets: ResMut<
cell_assets.pitches.insert(cell_pitch, handle); cell_assets.pitches.insert(cell_pitch, handle);
}); });
}); });
debug_assert!(cell_assets.pitches.iter().len() == 25); debug_assert_eq!(cell_assets.pitches.iter().len(), 132);
} }
fn control_volume( fn control_volume(
@ -798,16 +831,22 @@ fn load_materials(
mut cell_assets: ResMut<CellAssets>, mut cell_assets: ResMut<CellAssets>,
mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
let vertical = [-2, -1, 0, 1, 2]; // Up to 11 octaves total
let horizontal = [-2, -1, 0, 1, 2]; let vertical = -5..6;
debug_assert_eq!(vertical.len(), 11);
// 12 total colors supported
let horizontal = -6..6;
debug_assert_eq!(horizontal.len(), 12);
let tile_handle = server.load(TILE); let tile_handle = server.load(TILE);
vertical.iter().for_each(|&x| { vertical.for_each(|x| {
horizontal.iter().for_each(|&y| { horizontal.clone().for_each(|y| {
let c = Coordinates { x, y }; let c = Coordinates { x, y };
let pitch = c.as_cell_pitch(); let pitch = c.to_cell_pitch();
let handle = { let handle = {
let color = c.as_color(); let color = pitch.to_color();
let texture = Some(tile_handle.clone()); let texture = Some(tile_handle.clone());
let color_material = ColorMaterial { let color_material = ColorMaterial {
color, color,
@ -819,7 +858,7 @@ fn load_materials(
cell_assets.materials.insert(pitch, handle); cell_assets.materials.insert(pitch, handle);
}); });
}); });
debug_assert!(cell_assets.materials.iter().len() == 25); debug_assert_eq!(cell_assets.materials.iter().len(), 132);
} }
fn load_meshes(mut cell_assets: ResMut<CellAssets>, mut meshes: ResMut<Assets<Mesh>>) { fn load_meshes(mut cell_assets: ResMut<CellAssets>, mut meshes: ResMut<Assets<Mesh>>) {
@ -931,4 +970,58 @@ impl PitchMap {
self.notes.push(n.clone()); self.notes.push(n.clone());
} }
} }
/// Given the currently selected notes and octave range selected in the PitchMap convert the given coordinates to a CellPitch
fn coordinates_cell_pitch(&self, c: &Coordinates) -> CellPitch {
let note: Note = {
// Note is the x-axis pattern, repeating every 12 tiles because we support 12 pitches
let idx = (c.x as usize) % self.notes.len();
// TODO: pretty sure this doesn't handle negative numbers at all...
// need to shift by ?? first
self.notes.get(idx).unwrap().clone()
};
let octave: isize = {
// Octave is the y-axis pattern, repeating every 11 tiles because we support 11 octaves
let total_octaves = self.octaves_up as isize + self.octaves_down as isize + 1;
// TODO: pretty sure this doesn't handle negative numbers at all...
// need to shift by ?? first
c.y % total_octaves
};
CellPitch { note, octave }
}
}
/// When the pitch map changes, update all cells to use the correct pitch (note + octave)
fn reassign_cell_pitch(
mut query: Query<(&mut CellPitch, &Coordinates), With<Cell>>,
pitch_map: Res<PitchMap>,
) {
query.iter_mut().for_each(|(mut cell_pitch, c)| {
*cell_pitch = pitch_map.coordinates_cell_pitch(c);
});
}
// When CellPitch is added/updated on a cell, update the material
fn update_material(
mut query: Query<
(&CellPitch, &mut MeshMaterial2d<ColorMaterial>),
Or<(Added<CellPitch>, Changed<CellPitch>)>,
>,
cell_assets: ResMut<CellAssets>,
) {
query.iter_mut().for_each(|(cell_pitch, mut material)| {
material.0 = cell_assets.materials.get(cell_pitch).unwrap().clone();
})
}
// When CellPitch is added/updated on a cell, update the Pitch
fn update_audio(
mut query: Query<(&CellPitch, Entity), Or<(Added<CellPitch>, Changed<CellPitch>)>>,
cell_assets: ResMut<CellAssets>,
mut commands: Commands,
) {
query.iter_mut().for_each(|(cell_pitch, e)| {
let h = cell_assets.pitches.get(cell_pitch).unwrap().clone();
commands.entity(e).insert(AudioPlayer(h));
});
} }

Loading…
Cancel
Save