mid refactor, saving my place

main
Elijah Voigt 2 weeks ago
parent 97aa5cb20d
commit ee8a70cc16

@ -1,7 +1,10 @@
use bevy::{
feathers::{containers::*, display::*, theme::ThemeProps},
feathers::{containers::*, theme::ThemeProps},
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::*;
@ -57,6 +60,9 @@ fn main() {
// TODO Visualize simulation run/pause buttons enable/disable
toggle_interactive::<AudioPlayback>,
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>))
@ -187,6 +193,10 @@ fn the_ui() -> impl Scene {
Transform
pane()
Children [
pane_header()
Children [
Text("Conways Game of Life With Sound") ThemedText,
],
pane_body()
Children [
subpane()
@ -399,43 +409,24 @@ impl Coordinates {
(Vec2::new(self.x as f32, self.y as f32) * SCALE).extend(0.0)
}
fn as_color(&self) -> Color {
// hue (0..360.0) evenly spaced x in -2..4
// chroma (0..1) evenly spaced y in -2..5
let hue = (360.0 / 5.0) * ((self.x + 2) % 5) as f32;
let chroma = (1.0 / 5.0) * ((self.y + 2) % 5) as f32;
let lightness = (1.0 / 5.0) * ((self.y + 2) % 5) as f32;
Color::Oklcha(Oklcha {
hue,
chroma,
lightness,
..default()
})
}
fn as_cell_pitch(&self) -> CellPitch {
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,
/// For the purposes of creating assests, what does the given coordinate translate to CellPitch wise
fn to_cell_pitch(&self) -> CellPitch {
let note = match self.x {
-6 => Note::A,
-5 => Note::ASharp,
-4 => Note::B,
-3 => Note::C,
-2 => Note::CSharp,
-1 => Note::D,
0 => Note::DSharp,
1 => Note::E,
2 => Note::F,
3 => Note::FSharp,
4 => Note::G,
5 => Note::GSharp,
_ => panic!("This shouldn't happen!"),
};
let octave = ((self.y + 2).abs() % 5) - 2;
debug_assert!((-2..=2).contains(&octave));
debug!(
"\t{:?}",
CellPitch {
note: note.clone(),
octave
}
);
let octave = self.y;
CellPitch { note, octave }
}
}
@ -619,9 +610,9 @@ fn new_cell(
Coordinates { x, y }
template_value(playback_settings)
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(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 {
note: Note,
octave: isize,
@ -739,17 +730,59 @@ impl CellPitch {
// Thanks Claude Opus 4.8
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>) {
let notes = [Note::A, Note::B, Note::CSharp, Note::E, Note::FSharp];
let octaves = [-2, -1, 0, 1, 2];
notes.iter().for_each(|note| {
octaves.iter().for_each(|octave| {
let notes = [
Note::A,
Note::ASharp,
Note::B,
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 {
note: note.clone(),
octave: *octave,
octave,
};
let frequency = cell_pitch.to_frequency();
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);
});
});
debug_assert!(cell_assets.pitches.iter().len() == 25);
debug_assert_eq!(cell_assets.pitches.iter().len(), 132);
}
fn control_volume(
@ -798,16 +831,22 @@ fn load_materials(
mut cell_assets: ResMut<CellAssets>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
let vertical = [-2, -1, 0, 1, 2];
let horizontal = [-2, -1, 0, 1, 2];
// Up to 11 octaves total
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);
vertical.iter().for_each(|&x| {
horizontal.iter().for_each(|&y| {
vertical.for_each(|x| {
horizontal.clone().for_each(|y| {
let c = Coordinates { x, y };
let pitch = c.as_cell_pitch();
let pitch = c.to_cell_pitch();
let handle = {
let color = c.as_color();
let color = pitch.to_color();
let texture = Some(tile_handle.clone());
let color_material = ColorMaterial {
color,
@ -819,7 +858,7 @@ fn load_materials(
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>>) {
@ -931,4 +970,58 @@ impl PitchMap {
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