Skip to content

Commit 27cec2c

Browse files
committed
note placement
1 parent e3f980f commit 27cec2c

File tree

5 files changed

+115
-8
lines changed

5 files changed

+115
-8
lines changed

src/chart/beat.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
use std::{cmp::Ordering, ops::{Add, Sub}};
1+
use std::{
2+
cmp::Ordering,
3+
ops::{Add, Sub},
4+
};
25

36
use num::{FromPrimitive, Rational32};
47
use serde::{Deserialize, Serialize};
58

69
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
710
pub struct Beat(i32, Rational32);
811

12+
impl Beat {
13+
pub fn attach_to_beat_line(&mut self, density: u32) {
14+
let original_fraction = self.1;
15+
let target_denominator = density as i32;
16+
let closest_numerator = (original_fraction.numer() * target_denominator
17+
+ original_fraction.denom() / 2)
18+
/ original_fraction.denom();
19+
self.1 = Rational32::new(closest_numerator, target_denominator);
20+
}
21+
}
22+
923
impl Beat {
1024
pub const ZERO: Self = Beat(0, Rational32::ZERO);
1125
pub const ONE: Self = Beat(1, Rational32::ZERO);
@@ -36,7 +50,7 @@ impl From<Rational32> for Beat {
3650
}
3751

3852
impl Beat {
39-
pub fn new(whole: i32, ratio: Rational32) -> Self{
53+
pub fn new(whole: i32, ratio: Rational32) -> Self {
4054
Self(whole, ratio)
4155
}
4256

@@ -86,7 +100,7 @@ impl PartialOrd for Beat {
86100
match self.0.partial_cmp(&other.0) {
87101
Some(core::cmp::Ordering::Equal) => self.1.partial_cmp(&other.1),
88102
ord => ord,
89-
}
103+
}
90104
}
91105
}
92106

src/editing/create_note.rs

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use bevy::prelude::*;
2+
3+
use crate::{
4+
chart::{
5+
beat::Beat,
6+
note::{Note, NoteBundle, NoteKind},
7+
},
8+
constants::CANVAS_WIDTH,
9+
selection::SelectedLine,
10+
tab::timeline::{Timeline, TimelineSettings, TimelineViewport},
11+
timing::BpmList,
12+
};
13+
14+
pub fn create_note_system(
15+
mut commands: Commands,
16+
timeline: Timeline,
17+
keyboard: Res<ButtonInput<KeyCode>>,
18+
19+
selected_line: Res<SelectedLine>,
20+
21+
window_query: Query<&Window>,
22+
bpm_list: Res<BpmList>,
23+
24+
timeline_viewport: Res<TimelineViewport>,
25+
timeline_settings: Res<TimelineSettings>,
26+
) {
27+
let window = window_query.single();
28+
let Some(cursor_position) = window.cursor_position() else {
29+
return;
30+
};
31+
32+
let note_timeline_viewport = timeline_viewport.note_timeline_viewport();
33+
34+
if !note_timeline_viewport.contains(cursor_position) {
35+
return;
36+
}
37+
38+
let mut spawn_note = |kind: NoteKind| {
39+
let time = timeline.y_to_time(cursor_position.y);
40+
let mut beat = bpm_list.beat_at(time);
41+
beat.attach_to_beat_line(timeline_settings.density);
42+
43+
let x = (cursor_position.x - note_timeline_viewport.min.x) / note_timeline_viewport.width()
44+
- 0.5;
45+
46+
commands.entity(selected_line.0).with_children(|parent| {
47+
parent.spawn(NoteBundle::new(Note::new(
48+
kind,
49+
true,
50+
beat,
51+
x * CANVAS_WIDTH,
52+
)));
53+
});
54+
};
55+
56+
if keyboard.just_pressed(KeyCode::KeyQ) {
57+
spawn_note(NoteKind::Tap);
58+
}
59+
60+
if keyboard.just_pressed(KeyCode::KeyW) {
61+
spawn_note(NoteKind::Drag);
62+
}
63+
64+
if keyboard.just_pressed(KeyCode::KeyE) {
65+
spawn_note(NoteKind::Flick);
66+
}
67+
68+
if keyboard.just_pressed(KeyCode::KeyR) {
69+
// TODO: make hold placement done with 2 `R` press
70+
spawn_note(NoteKind::Hold {
71+
hold_beat: Beat::ONE,
72+
});
73+
}
74+
}

src/editing/mod.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use bevy::prelude::*;
2+
3+
use crate::project::project_loaded;
4+
5+
use self::create_note::create_note_system;
6+
7+
mod create_note;
8+
9+
pub struct EditingPlugin;
10+
11+
impl Plugin for EditingPlugin {
12+
fn build(&self, app: &mut App) {
13+
app.add_systems(Update, create_note_system.run_if(project_loaded()));
14+
}
15+
}

src/main.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ mod assets;
22
mod audio;
33
mod chart;
44
mod constants;
5+
mod editing;
56
mod exporter;
7+
mod file;
68
mod hit_sound;
79
mod home;
810
mod loader;
@@ -15,14 +17,15 @@ mod serialzation;
1517
mod tab;
1618
mod timing;
1719
mod translation;
18-
mod file;
1920

2021
use crate::assets::AssetsPlugin;
2122
use crate::audio::AudioPlugin;
2223
use crate::chart::event::LineEvent;
2324
use crate::chart::note::Note;
25+
use crate::editing::EditingPlugin;
2426
use crate::exporter::phichain::PhiChainExporter;
2527
use crate::exporter::Exporter;
28+
use crate::file::FilePickingPlugin;
2629
use crate::home::HomePlugin;
2730
use crate::misc::MiscPlugin;
2831
use crate::misc::WorkingDirectory;
@@ -49,7 +52,6 @@ use bevy_egui::egui::{Color32, Frame};
4952
use bevy_egui::{EguiContext, EguiPlugin};
5053
use bevy_mod_picking::prelude::*;
5154
use egui_dock::{DockArea, DockState, NodeIndex, Style};
52-
use file::FilePickingPlugin;
5355

5456
fn main() {
5557
App::new()
@@ -68,6 +70,7 @@ fn main() {
6870
.add_plugins(crate::selection::SelectionPlugin)
6971
.add_plugins(MiscPlugin)
7072
.add_plugins(TabPlugin)
73+
.add_plugins(EditingPlugin)
7174
.add_plugins(FrameTimeDiagnosticsPlugin::default())
7275
.add_plugins(AssetsPlugin)
7376
.add_plugins(TranslationPlugin)

src/tab/timeline.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use bevy::{ecs::system::SystemParam, prelude::*};
22
use egui::{Color32, Ui};
3+
use num::Rational32;
34
use url::Url;
45

56
use crate::{
@@ -205,14 +206,14 @@ impl TimelineViewport {
205206
#[derive(Resource)]
206207
pub struct TimelineSettings {
207208
pub zoom: f32,
208-
pub density: f32,
209+
pub density: u32,
209210
}
210211

211212
impl Default for TimelineSettings {
212213
fn default() -> Self {
213214
Self {
214215
zoom: 2.0,
215-
density: 4.0,
216+
density: 4,
216217
}
217218
}
218219
}
@@ -243,7 +244,7 @@ impl<'w> Timeline<'w> {
243244

244245
let interval = self
245246
.bpm_list
246-
.time_at(Beat::from(1.0 / self.timeline_settings.density));
247+
.time_at(Beat::new(0, Rational32::new(1, self.timeline_settings.density as i32)));
247248

248249
std::iter::repeat(0)
249250
.take((audio_duration / interval).round() as usize)

0 commit comments

Comments
 (0)