Still in the middle of rewriting ...

This commit is contained in:
Stepland 2022-03-17 02:50:30 +01:00
parent b6fff2914d
commit b689c1ede5
19 changed files with 465 additions and 317 deletions

View File

@ -87,6 +87,39 @@ namespace better {
return tail_tip; return tail_tip;
}; };
const auto abs_diff = [](const auto& a, const auto& b){
if (a > b) {
return a - b;
} else {
return b - a;
}
};
unsigned int LongNote::get_tail_length() const {
if (position.get_x() == tail_tip.get_x()) {
return abs_diff(position.get_y(), tail_tip.get_y());
} else {
return abs_diff(position.get_x(), tail_tip.get_x());
}
}
unsigned int LongNote::get_tail_angle() const {
if (position.get_x() == tail_tip.get_x()) {
if (position.get_y() > tail_tip.get_y()) {
return 0;
} else {
return 180;
}
} else {
if (position.get_x() > tail_tip.get_x()) {
return 270;
} else {
return 90;
}
}
}
auto _time_bounds = VariantVisitor { auto _time_bounds = VariantVisitor {
[](const TapNote& t) -> std::pair<Fraction, Fraction> { return {t.get_time(), t.get_time()}; }, [](const TapNote& t) -> std::pair<Fraction, Fraction> { return {t.get_time(), t.get_time()}; },
[](const LongNote& l) -> std::pair<Fraction, Fraction> { return {l.get_time(), l.get_end()}; }, [](const LongNote& l) -> std::pair<Fraction, Fraction> { return {l.get_time(), l.get_end()}; },

View File

@ -57,6 +57,8 @@ namespace better {
Fraction get_end() const; Fraction get_end() const;
Fraction get_duration() const; Fraction get_duration() const;
Position get_tail_tip() const; Position get_tail_tip() const;
unsigned int get_tail_length() const;
unsigned int get_tail_angle() const;
private: private:
Fraction time; Fraction time;
Position position; Position position;

View File

@ -63,5 +63,6 @@ namespace better {
decltype(order_by_difficulty_name) decltype(order_by_difficulty_name)
> charts{order_by_difficulty_name}; > charts{order_by_difficulty_name};
Metadata metadata; Metadata metadata;
Timing timing;
}; };
} }

View File

@ -117,19 +117,49 @@ namespace better {
change change
*/ */
Fraction Timing::fractional_seconds_at(Fraction beats) const { Fraction Timing::fractional_seconds_at(Fraction beats) const {
auto bpm_change = this->events_by_beats auto bpm_change = this->events_by_beats.upper_bound(BPMEvent(beats, 0, 0));
.upper_bound(BPMEvent(beats, 0, 0));
if (bpm_change != this->events_by_beats.begin()) { if (bpm_change != this->events_by_beats.begin()) {
bpm_change = std::prev(bpm_change); bpm_change = std::prev(bpm_change);
} }
auto beats_since_previous_event = beats - bpm_change->get_beats(); auto beats_since_previous_event = beats - bpm_change->get_beats();
auto seconds_since_previous_event = auto seconds_since_previous_event = (
(60 * beats_since_previous_event) / bpm_change->get_bpm(); Fraction{60}
* beats_since_previous_event
/ bpm_change->get_bpm()
);
return bpm_change->get_seconds() + seconds_since_previous_event; return bpm_change->get_seconds() + seconds_since_previous_event;
}; };
Fraction Timing::fractional_seconds_between(
Fraction beat_a,
Fraction beat_b
) const {
return (
fractional_seconds_at(beat_b)
- fractional_seconds_at(beat_a)
);
};
sf::Time Timing::time_at(Fraction beats) const { sf::Time Timing::time_at(Fraction beats) const {
auto microseconds = fractional_seconds_at(beats) * 1000000; return frac_to_time(fractional_seconds_at(beats));
return sf::microseconds(microseconds.convert_to<sf::Int64>()); };
sf::Time Timing::time_between(Fraction beat_a, Fraction beat_b) const {
return frac_to_time(fractional_seconds_between(beat_a, beat_b));
};
Fraction Timing::beats_at(sf::Time time) const {
Fraction fractional_seconds{time.asMicroseconds(), 1000000};
auto bpm_change = this->events_by_seconds.upper_bound(BPMEvent(0, fractional_seconds, 0));
if (bpm_change != this->events_by_seconds.begin()) {
bpm_change = std::prev(bpm_change);
} }
auto seconds_since_previous_event = fractional_seconds - bpm_change->get_seconds();
auto beats_since_previous_event = (
bpm_change->get_bpm()
* seconds_since_previous_event
/ Fraction{60}
);
return bpm_change->get_beats() + beats_since_previous_event;
};
} }

View File

@ -48,10 +48,19 @@ namespace better {
Timing(const std::vector<BPMAtBeat>& events, const SecondsAtBeat& offset); Timing(const std::vector<BPMAtBeat>& events, const SecondsAtBeat& offset);
Fraction fractional_seconds_at(Fraction beats) const; Fraction fractional_seconds_at(Fraction beats) const;
Fraction fractional_seconds_between(Fraction beat_a, Fraction beat_b) const;
sf::Time time_at(Fraction beats) const; sf::Time time_at(Fraction beats) const;
sf::Time time_between(Fraction beat_a, Fraction beat_b) const;
Fraction beats_at(sf::Time time) const;
private: private:
std::set<BPMEvent, decltype(order_by_beats)> events_by_beats{order_by_beats}; std::set<BPMEvent, decltype(order_by_beats)> events_by_beats{order_by_beats};
std::set<BPMEvent, decltype(order_by_seconds)> events_by_seconds{order_by_seconds}; std::set<BPMEvent, decltype(order_by_seconds)> events_by_seconds{order_by_seconds};
}; };
const auto frac_to_time = [](const Fraction& f) {
auto microseconds = f * 1000000;
return sf::microseconds(microseconds.convert_to<sf::Int64>());
};
} }

View File

@ -1,4 +1,5 @@
#include "chart_state.hpp" #include "chart_state.hpp"
#include "src/better_note.hpp"
ChartState::ChartState(better::Chart& c, std::filesystem::path assets) : ChartState::ChartState(better::Chart& c, std::filesystem::path assets) :
chart(c), chart(c),
@ -7,9 +8,12 @@ ChartState::ChartState(better::Chart& c, std::filesystem::path assets) :
history.push(std::make_shared<OpenChart>(c)); history.push(std::make_shared<OpenChart>(c));
} }
std::optional<Note> ChartState::makeLongNoteDummy(int current_tick) const { std::optional<better::LongNote> ChartState::make_long_note_dummy(Fraction current_beat) const {
if (creating_long_note and long_note_being_created) { if (creating_long_note and long_note_being_created) {
Note long_note = Note(long_note_being_created->first, long_note_being_created->second); better::LongNote long_note{
long_note_being_created->first,
long_note_being_created->second
};
Note dummy_long_note = Note( Note dummy_long_note = Note(
long_note.getPos(), long_note.getPos(),
current_tick, current_tick,
@ -21,7 +25,7 @@ std::optional<Note> ChartState::makeLongNoteDummy(int current_tick) const {
} }
} }
std::optional<Note> ChartState::makeCurrentLongNote() const { std::optional<better::LongNote> ChartState::make_current_long_note() const {
if (creating_long_note and long_note_being_created) { if (creating_long_note and long_note_being_created) {
return Note(long_note_being_created->first, long_note_being_created->second); return Note(long_note_being_created->first, long_note_being_created->second);
} else { } else {

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "better_note.hpp"
#include "better_song.hpp" #include "better_song.hpp"
#include "history.hpp" #include "history.hpp"
#include "history_actions.hpp" #include "history_actions.hpp"
@ -15,11 +16,11 @@ struct ChartState {
std::set<Note> selected_notes; std::set<Note> selected_notes;
NotesClipboard notes_clipboard; NotesClipboard notes_clipboard;
SelectionState time_selection; SelectionState time_selection;
std::optional<std::pair<Note, Note>> long_note_being_created; std::optional<better::LongNote> long_note_being_created;
bool creating_long_note; bool creating_long_note;
History<std::shared_ptr<ActionWithMessage>> history; History<std::shared_ptr<ActionWithMessage>> history;
DensityGraph density_graph; DensityGraph density_graph;
std::optional<Note> makeLongNoteDummy(int current_tick) const; std::optional<better::LongNote> make_long_note_dummy(Fraction current_beat) const;
std::optional<Note> makeCurrentLongNote() const; std::optional<better::LongNote> make_current_long_note() const;
}; };

View File

@ -1,5 +1,6 @@
#include "editor_state.hpp" #include "editor_state.hpp"
#include <SFML/System/Time.hpp>
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <filesystem> #include <filesystem>
@ -8,6 +9,27 @@
#include <imgui_internal.h> #include <imgui_internal.h>
#include <imgui_stdlib.h> #include <imgui_stdlib.h>
#include <tinyfiledialogs.h> #include <tinyfiledialogs.h>
#include "src/time_interval.hpp"
EditorState::EditorState(
const better::Song& song_,
const std::filesystem::path& assets_,
const std::filesystem::path& song_path = {}
) :
song(song_),
song_path(song_path),
playfield(assets_),
linear_view(assets_),
music_path_in_gui(song.metadata.audio.value_or("")),
applicable_timing(song.timing),
assets(assets_)
{
if (not song.charts.empty()) {
open_chart(this->song.charts.begin()->second);
}
reload_music();
reload_album_cover();
};
/* /*
* Reloads music from what's indicated in the "music path" field of the song * Reloads music from what's indicated in the "music path" field of the song
@ -15,47 +37,47 @@
* Updates playbackPosition and preview_end as well * Updates playbackPosition and preview_end as well
*/ */
void EditorState::reload_music() { void EditorState::reload_music() {
const auto absolute_music_path = song_path.parent_path() / edited_music_path; if (not song_path.has_value()) {
music_state.reset();
return;
}
const auto absolute_music_path = song_path->parent_path() / music_path_in_gui;
try { try {
music_state.emplace(absolute_music_path); music_state.emplace(absolute_music_path);
} catch (const std::exception& e) { } catch (const std::exception& e) {
music_state.reset(); music_state.reset();
} }
reload_preview_end(); reload_editable_range();
playback_position = std::clamp(
auto preview_start = sf::Time::Zero; playback_position,
if (chart_state) { editable_range.start,
preview_start = std::min(preview_start, chart_state->chart.timing.time_at(0)); editable_range.end
} );
playback_position = std::clamp(playback_position, preview_start, preview_end); previous_playback_position = playback_position;
previous_pos = playback_position;
} }
void EditorState::reload_preview_end() { void EditorState::reload_editable_range() {
auto old_preview_end = this->preview_end; auto old_range = this->editable_range;
sf::Time music_duration = sf::Time::Zero; TimeInterval new_range;
if (music_state) { if (music_state) {
music_duration = music_state->getDuration(); new_range += music_state->music.getDuration();
}
if (chart_state) {
new_range += chart_state->chart.time_of_last_event().value_or(sf::Time::Zero);
} }
float chart_end = 0; new_range.end += sf::seconds(10);
if (chart) {
chart_end = chart->ref // If there is no music, make sure we can edit at least the first whole minute
.time_of_last_event() if (not music_state) {
.value_or(sf::Time::Zero) new_range += sf::seconds(60);
.asSeconds();
} }
float preview_end_seconds = std::max(music_duration, chart_end); this->editable_range = new_range;
if (old_range != new_range and this->chart_state.has_value()) {
// Add some extra time at the end to allow for more notes to be placed chart_state->density_graph.should_recompute = true;
// after the end of the chart
// TODO: is this really the way to do it ?
preview_end_seconds += 2.f;
this->preview_end = sf::seconds(preview_end_seconds);
if (old_preview_end != this->preview_end and this->chart.has_value()) {
chart->density_graph.should_recompute = true;
} }
} }
@ -64,38 +86,56 @@ void EditorState::reload_preview_end() {
* of the song Resets the album cover state if anything fails * of the song Resets the album cover state if anything fails
*/ */
void EditorState::reload_album_cover() { void EditorState::reload_album_cover() {
album_cover.emplace(); if (not song_path.has_value() or not song.metadata.jacket.has_value()) {
jacket.reset();
return;
}
std::filesystem::path album_cover_path = jacket.emplace();
std::filesystem::path(song.path).parent_path() / song.albumCoverPath; auto jacket_path = song_path->parent_path() / *song.metadata.jacket;
if (song.albumCoverPath.empty() or not std::filesystem::exists(album_cover_path) if (
or not album_cover->loadFromFile(album_cover_path.string())) { not std::filesystem::exists(jacket_path)
album_cover.reset(); or not jacket->loadFromFile(jacket_path.string())
) {
jacket.reset();
} }
} }
void EditorState::set_playback_and_music_position(sf::Time newPosition) { void EditorState::set_playback_position(sf::Time newPosition) {
reload_preview_end(); newPosition = std::clamp(newPosition, editable_range.start, editable_range.end);
previous_playback_position = newPosition - (sf::seconds(1) / 60.f);
newPosition = sf::seconds(
std::clamp(
newPosition.asSeconds(),
-song.offset,
this->preview_end.asSeconds()
)
);
previous_pos = sf::seconds(newPosition.asSeconds() - 1.f / 60.f);
playback_position = newPosition; playback_position = newPosition;
if (music) { if (music_state) {
if (playback_position.asSeconds() >= 0 and playback_position < music->getDuration()) { if (
music->setPlayingOffset(playback_position); playback_position >= sf::Time::Zero
and playback_position < music_state->music.getDuration()
) {
music_state->music.setPlayingOffset(playback_position);
} else { } else {
music->stop(); music_state->music.stop();
} }
} }
} }
float EditorState::current_beats() {
return beats_at(playback_position);
};
float EditorState::beats_at(sf::Time time) {
auto frac_beats = applicable_timing.beats_at(time);
return static_cast<float>(frac_beats);
};
float EditorState::seconds_at(Fraction beat) {
auto frac_seconds = applicable_timing.fractional_seconds_at(beat);
return static_cast<float>(frac_seconds);
};
Fraction EditorState::get_snap_step() {
return Fraction{1, snap};
};
void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndingState) { void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndingState) {
ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_Once); ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_Once);
ImGui::SetNextWindowSizeConstraints( ImGui::SetNextWindowSizeConstraints(
@ -104,32 +144,34 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
Toolbox::CustomConstraints::ContentSquare); Toolbox::CustomConstraints::ContentSquare);
if (ImGui::Begin("Playfield", &showPlayfield, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { if (ImGui::Begin("Playfield", &showPlayfield, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
if (not ImGui::IsWindowHovered() and chart and chart->creating_long_note) { if (
not ImGui::IsWindowHovered()
and chart_state
and chart_state->creating_long_note
) {
// cancel long note creation if the mouse is or goes out of the playfield // cancel long note creation if the mouse is or goes out of the playfield
chart->long_note_being_created.reset(); chart_state->long_note_being_created.reset();
chart->creating_long_note = false; chart_state->creating_long_note = false;
} }
float squareSize = ImGui::GetWindowSize().x / 4.f; float squareSize = ImGui::GetWindowSize().x / 4.f;
float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x; float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x;
int ImGuiIndex = 0; int ImGuiIndex = 0;
if (chart) { if (chart_state) {
playfield.resize(static_cast<unsigned int>(ImGui::GetWindowSize().x)); playfield.resize(static_cast<unsigned int>(ImGui::GetWindowSize().x));
if (chart_state->long_note_being_created) {
auto longNoteDummy =
chart->makeLongNoteDummy(static_cast<int>(roundf(getCurrentTick())));
if (longNoteDummy) {
playfield.drawLongNote( playfield.drawLongNote(
*longNoteDummy, *chart_state->long_note_being_created,
playback_position, playback_position,
getCurrentTick(), getCurrentTick(),
song.BPM, song.BPM,
getResolution()); getResolution()
);
} }
for (auto const& note : visibleNotes) { for (auto const& note : visibleNotes) {
float note_offset = float note_offset =
(playback_position.asSeconds() - getSecondsAt(note.getTiming())); (playback_position.asSeconds() - seconds_at(note.getTiming()));
// auto frame = static_cast<long long // auto frame = static_cast<long long
// int>(std::floor(note_offset * 30.f)); // int>(std::floor(note_offset * 30.f));
int x = note.getPos() % 4; int x = note.getPos() % 4;
@ -152,7 +194,7 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
playfield.drawLongNote( playfield.drawLongNote(
note, note,
playback_position, playback_position,
getCurrentTick(), current_tick(),
song.BPM, song.BPM,
getResolution(), getResolution(),
marker, marker,
@ -161,9 +203,9 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
} }
ImGui::SetCursorPos({0, TitlebarHeight}); ImGui::SetCursorPos({0, TitlebarHeight});
ImGui::Image(playfield.longNoteLayer); ImGui::Image(playfield.long_note_layer);
ImGui::SetCursorPos({0, TitlebarHeight}); ImGui::SetCursorPos({0, TitlebarHeight});
ImGui::Image(playfield.markerLayer); ImGui::Image(playfield.marker_layer);
} }
// Display button grid // Display button grid
@ -181,11 +223,11 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
if (ImGui::ImageButton(playfield.button, {squareSize, squareSize}, 0)) { if (ImGui::ImageButton(playfield.button, {squareSize, squareSize}, 0)) {
toggleNoteAtCurrentTime(x + 4 * y); toggleNoteAtCurrentTime(x + 4 * y);
} }
if (ImGui::IsItemHovered() and chart and chart->creating_long_note) { if (ImGui::IsItemHovered() and chart_state and chart_state->creating_long_note) {
// Deal with long note creation stuff // Deal with long note creation stuff
if (not chart->long_note_being_created) { if (not chart->long_note_being_created) {
Note current_note = Note current_note =
Note(x + 4 * y, static_cast<int>(roundf(getCurrentTick()))); Note(x + 4 * y, static_cast<int>(roundf(current_tick())));
chart->long_note_being_created = chart->long_note_being_created =
std::make_pair(current_note, current_note); std::make_pair(current_note, current_note);
} else { } else {
@ -248,8 +290,8 @@ void EditorState::displayProperties() {
{ {
ImGui::Columns(2, nullptr, false); ImGui::Columns(2, nullptr, false);
if (album_cover) { if (jacket) {
ImGui::Image(*album_cover, sf::Vector2f(200, 200)); ImGui::Image(*jacket, sf::Vector2f(200, 200));
} else { } else {
ImGui::BeginChild("Album Cover", ImVec2(200, 200), true); ImGui::BeginChild("Album Cover", ImVec2(200, 200), true);
ImGui::EndChild(); ImGui::EndChild();
@ -266,7 +308,7 @@ void EditorState::displayProperties() {
reload_music(); reload_music();
} }
if (Toolbox::InputTextColored( if (Toolbox::InputTextColored(
album_cover.has_value(), jacket.has_value(),
"Invalid Album Cover Path", "Invalid Album Cover Path",
"Album Cover", "Album Cover",
&song.albumCoverPath)) { &song.albumCoverPath)) {
@ -302,7 +344,7 @@ void EditorState::displayStatus() {
} }
} }
if (not album_cover) { if (not jacket) {
if (not song.albumCoverPath.empty()) { if (not song.albumCoverPath.empty()) {
ImGui::TextColored( ImGui::TextColored(
ImVec4(1, 0.42, 0.41, 1), ImVec4(1, 0.42, 0.41, 1),
@ -347,7 +389,7 @@ void EditorState::displayPlaybackStatus() {
ImGui::SameLine(); ImGui::SameLine();
ImGui::TextColored(ImVec4(0.53, 0.53, 0.53, 1), "Beats :"); ImGui::TextColored(ImVec4(0.53, 0.53, 0.53, 1), "Beats :");
ImGui::SameLine(); ImGui::SameLine();
ImGui::Text("%02.2f", this->getBeats()); ImGui::Text("%02.2f", this->get_current_beats());
ImGui::SameLine(); ImGui::SameLine();
if (music) { if (music) {
ImGui::TextColored( ImGui::TextColored(
@ -590,9 +632,14 @@ void EditorState::toggleNoteAtCurrentTime(int pos) {
} }
} }
const sf::Time& EditorState::get_preview_end() { const TimeInterval& EditorState::get_editable_range() {
reload_preview_end(); reload_editable_range();
return preview_end; return editable_range;
}
void EditorState::open_chart(better::Chart& chart) {
chart_state.emplace(chart, assets);
} }
void ESHelper::save(EditorState& ed) { void ESHelper::save(EditorState& ed) {

View File

@ -12,6 +12,7 @@
#include "music_state.hpp" #include "music_state.hpp"
#include "notes_clipboard.hpp" #include "notes_clipboard.hpp"
#include "precise_music.hpp" #include "precise_music.hpp"
#include "time_interval.hpp"
#include "time_selection.hpp" #include "time_selection.hpp"
#include "widgets/linear_view.hpp" #include "widgets/linear_view.hpp"
#include "widgets/playfield.hpp" #include "widgets/playfield.hpp"
@ -28,29 +29,19 @@ enum saveChangesResponses {
/* /*
* The god class, holds everything there is to know about the currently open * The god class, holds everything there is to know about the currently open
* .memon file * file
*/ */
class EditorState { class EditorState {
public: public:
EditorState( EditorState(
const better::Song& song, const better::Song& song,
const std::filesystem::path& song_path, const std::filesystem::path& assets,
const std::filesystem::path& assets const std::filesystem::path& save_path
) : );
song(song),
playfield(assets),
linear_view(assets),
edited_music_path(song.metadata.audio.value_or("")),
song_path(song_path)
{
if (not this->song.charts.empty()) {
this->chart_state.emplace(this->song.charts.begin()->second, assets);
}
reload_music();
reload_album_cover();
};
better::Song song; better::Song song;
std::optional<std::filesystem::path> song_path;
std::optional<ChartState> chart_state; std::optional<ChartState> chart_state;
std::optional<MusicState> music_state; std::optional<MusicState> music_state;
@ -58,38 +49,23 @@ public:
Playfield playfield; Playfield playfield;
LinearView linear_view; LinearView linear_view;
// the snap but divided by 4 because you can't set a snap to anything lower
// than 4ths
int snap = 1; int snap = 1;
std::optional<sf::Texture> album_cover; std::optional<sf::Texture> jacket;
bool playing; bool playing;
sf::Time previous_pos; sf::Time previous_playback_position;
sf::Time playback_position; sf::Time playback_position;
const sf::Time& get_preview_end(); const TimeInterval& get_editable_range();
void set_playback_and_music_position(sf::Time new_position); void set_playback_position(sf::Time new_position);
float getBeats() { return getBeatsAt(playback_position.asSeconds()); }; float current_beats();
float getBeatsAt(float seconds) { float beats_at(sf::Time time);
return ((seconds + song.offset) / 60.f) * song.BPM; float seconds_at(Fraction beat);
}; Fraction get_snap_step();
float getCurrentTick() { return getTicksAt(playback_position.asSeconds()); };
float getTicksAt(float seconds) {
return getBeatsAt(seconds) * get_resolution();
}
float getSecondsAt(int tick) {
return (60.f * tick) / (song.BPM * get_resolution()) - song.offset;
};
int get_resolution() { return chart_state ? chart_state->chart.getResolution() : 240; };
int get_snap_step() { return get_resolution() / snap; };
void reload_album_cover();
void reload_preview_end();
bool showPlayfield = true; bool showPlayfield = true;
bool showProperties; bool showProperties;
@ -120,13 +96,25 @@ public:
void toggleNoteAtCurrentTime(int pos); void toggleNoteAtCurrentTime(int pos);
private: private:
sf::Time preview_end; // sf::Time (in the audio file "coordinates") at which the chart preview stops, can be /*
// after the end of the actual audio file sf::Time bounds (in the audio file "coordinates") which are accessible
(and maybe editable) from the editor, can extend before and after
the actual audio file
*/
TimeInterval editable_range;
std::string edited_music_path; void reload_album_cover();
void reload_editable_range();
std::string music_path_in_gui;
void reload_music(); void reload_music();
std::filesystem::path song_path; better::Timing& applicable_timing;
void reload_applicable_timing();
void open_chart(better::Chart& chart);
std::filesystem::path assets;
}; };
namespace ESHelper { namespace ESHelper {

View File

@ -2,7 +2,7 @@
void Move::backwardsInTime(std::optional<EditorState>& ed) { void Move::backwardsInTime(std::optional<EditorState>& ed) {
if (ed and ed->chart) { if (ed and ed->chart) {
float floatTicks = ed->getCurrentTick(); float floatTicks = ed->current_tick();
auto prevTick = static_cast<int>(floorf(floatTicks)); auto prevTick = static_cast<int>(floorf(floatTicks));
int step = ed->get_snap_step(); int step = ed->get_snap_step();
int prevTickInSnap = prevTick; int prevTickInSnap = prevTick;
@ -11,17 +11,17 @@ void Move::backwardsInTime(std::optional<EditorState>& ed) {
} else { } else {
prevTickInSnap -= prevTick % step; prevTickInSnap -= prevTick % step;
} }
ed->setPlaybackAndMusicPosition(sf::seconds(ed->getSecondsAt(prevTickInSnap))); ed->setPlaybackAndMusicPosition(sf::seconds(ed->seconds_at(prevTickInSnap)));
} }
} }
void Move::forwardsInTime(std::optional<EditorState>& ed) { void Move::forwardsInTime(std::optional<EditorState>& ed) {
if (ed and ed->chart) { if (ed and ed->chart) {
float floatTicks = ed->getCurrentTick(); float floatTicks = ed->current_tick();
auto nextTick = static_cast<int>(ceilf(floatTicks)); auto nextTick = static_cast<int>(ceilf(floatTicks));
int step = ed->get_snap_step(); int step = ed->get_snap_step();
int nextTickInSnap = nextTick + (step - nextTick % step); int nextTickInSnap = nextTick + (step - nextTick % step);
ed->setPlaybackAndMusicPosition(sf::seconds(ed->getSecondsAt(nextTickInSnap))); ed->setPlaybackAndMusicPosition(sf::seconds(ed->seconds_at(nextTickInSnap)));
} }
} }
@ -81,7 +81,7 @@ void Edit::copy(std::optional<EditorState>& ed, NotificationsQueue& nq) {
void Edit::paste(std::optional<EditorState>& ed, NotificationsQueue& nq) { void Edit::paste(std::optional<EditorState>& ed, NotificationsQueue& nq) {
if (ed and ed->chart and (not ed->chart->notesClipboard.empty())) { if (ed and ed->chart and (not ed->chart->notesClipboard.empty())) {
auto tick_offset = static_cast<int>(ed->getCurrentTick()); auto tick_offset = static_cast<int>(ed->current_tick());
std::set<Note> pasted_notes = ed->chart->notesClipboard.paste(tick_offset); std::set<Note> pasted_notes = ed->chart->notesClipboard.paste(tick_offset);
std::stringstream ss; std::stringstream ss;

View File

@ -40,7 +40,7 @@ ToggledNotes::ToggledNotes(std::set<Note> n, bool have_been_added) :
} }
void ToggledNotes::doAction(EditorState& ed) const { void ToggledNotes::doAction(EditorState& ed) const {
ed.setPlaybackAndMusicPosition(sf::seconds(ed.getSecondsAt(notes.begin()->getTiming()))); ed.setPlaybackAndMusicPosition(sf::seconds(ed.seconds_at(notes.begin()->getTiming())));
if (have_been_added) { if (have_been_added) {
for (auto note : notes) { for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) { if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) {
@ -57,7 +57,7 @@ void ToggledNotes::doAction(EditorState& ed) const {
} }
void ToggledNotes::undoAction(EditorState& ed) const { void ToggledNotes::undoAction(EditorState& ed) const {
ed.setPlaybackAndMusicPosition(sf::seconds(ed.getSecondsAt(notes.begin()->getTiming()))); ed.setPlaybackAndMusicPosition(sf::seconds(ed.seconds_at(notes.begin()->getTiming())));
if (not have_been_added) { if (not have_been_added) {
for (auto note : notes) { for (auto note : notes) {
if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) { if (ed.chart->ref.Notes.find(note) == ed.chart->ref.Notes.end()) {

View File

@ -2,74 +2,60 @@
#include <cmath> #include <cmath>
LNMarker::LNMarker(std::filesystem::path folder) { LNMarker::LNMarker(std::filesystem::path folder) :
triangle_appearance = load_tex_with_prefix<16, 400>(folder, "LN0001_M"); triangle_appearance(load_tex_with_prefix<16, 400>(folder, "LN0001_M")),
triangle_begin_cycle = load_tex_with_prefix<8, 600>(folder, "LN0001_M"); triangle_begin_cycle(load_tex_with_prefix<8, 600>(folder, "LN0001_M")),
triangle_cycle = load_tex_with_prefix<16, 500>(folder, "LN0001_M"); triangle_cycle(load_tex_with_prefix<16, 500>(folder, "LN0001_M")),
square_highlight(load_tex_with_prefix<16, 300>(folder, "LN0001_M")),
square_highlight = load_tex_with_prefix<16, 300>(folder, "LN0001_M"); square_outline(load_tex_with_prefix<16, 100>(folder, "LN0001_M")),
square_outline = load_tex_with_prefix<16, 100>(folder, "LN0001_M"); square_background(load_tex_with_prefix<16, 200>(folder, "LN0001_M")),
square_background = load_tex_with_prefix<16, 200>(folder, "LN0001_M"); tail_cycle(load_tex_with_prefix<16, 0>(folder, "LN0001_M"))
{
tail_cycle = load_tex_with_prefix<16, 0>(folder, "LN0001_M");
setRepeated<16>(tail_cycle, true); setRepeated<16>(tail_cycle, true);
} }
std::optional<std::reference_wrapper<sf::Texture>> opt_tex_ref LNMarker::triangle_at(int frame) {
LNMarker::getTriangleTexture(float seconds) {
auto frame = static_cast<long long int>(std::floor(seconds * 30.f));
if (frame >= -16 and frame <= -1) { if (frame >= -16 and frame <= -1) {
// approach phase // approach phase
return triangle_appearance.at(static_cast<unsigned long long int>(16 + frame)); return triangle_appearance.at(16 + frame);
} else if (frame >= 0) { } else if (frame >= 0) {
if (frame <= 7) { if (frame <= 7) {
return triangle_begin_cycle.at(static_cast<unsigned long long int>(frame)); return triangle_begin_cycle.at(frame);
} else { } else {
return triangle_cycle.at(static_cast<unsigned long long int>((frame - 8) % 16)); return triangle_cycle.at((frame - 8) % 16);
} }
} else { } else {
return {}; return {};
} }
} }
std::optional<std::reference_wrapper<sf::Texture>> LNMarker::getTailTexture(float seconds) { opt_tex_ref LNMarker::tail_at(int frame) {
auto frame = static_cast<long long int>(std::floor(seconds * 30.f));
if (frame >= -16) { if (frame >= -16) {
return tail_cycle.at(static_cast<unsigned long long int>((16 + (frame % 16)) % 16)); return tail_cycle.at((16 + (frame % 16)) % 16);
} else { } else {
return {}; return {};
} }
} }
std::optional<std::reference_wrapper<sf::Texture>> opt_tex_ref LNMarker::highlight_at(int frame) {
LNMarker::getSquareHighlightTexture(float seconds) {
auto frame = static_cast<long long int>(std::floor(seconds * 30.f));
if (frame >= 0) { if (frame >= 0) {
return square_highlight.at( return square_highlight.at((16 + (frame % 16)) % 16);
static_cast<unsigned long long int>((16 + (frame % 16)) % 16));
} else { } else {
return {}; return {};
} }
} }
std::optional<std::reference_wrapper<sf::Texture>> opt_tex_ref LNMarker::outline_at(int frame) {
LNMarker::getSquareOutlineTexture(float seconds) {
auto frame = static_cast<long long int>(std::floor(seconds * 30.f));
if (frame >= -16) { if (frame >= -16) {
return square_outline.at( return square_outline.at((16 + (frame % 16)) % 16);
static_cast<unsigned long long int>((16 + (frame % 16)) % 16));
} else { } else {
return {}; return {};
} }
} }
std::optional<std::reference_wrapper<sf::Texture>> opt_tex_ref LNMarker::background_at(int frame) {
LNMarker::getSquareBackgroundTexture(float seconds) {
auto frame = static_cast<long long int>(std::floor(seconds * 30.f));
if (frame >= -16) { if (frame >= -16) {
return square_background.at( return square_background.at((16 + (frame % 16)) % 16);
static_cast<unsigned long long int>((16 + (frame % 16)) % 16));
} else { } else {
return {}; return {};
} }

View File

@ -8,6 +8,8 @@
#include <list> #include <list>
#include <map> #include <map>
using opt_tex_ref = std::optional<std::reference_wrapper<sf::Texture>>;
/* /*
* Stores every rotated variant of the long note marker * Stores every rotated variant of the long note marker
* This approach is absolutely terrible, I should just dig a little bit into the * This approach is absolutely terrible, I should just dig a little bit into the
@ -18,15 +20,11 @@ class LNMarker {
public: public:
explicit LNMarker(std::filesystem::path folder); explicit LNMarker(std::filesystem::path folder);
std::optional<std::reference_wrapper<sf::Texture>> getTriangleTexture(float seconds); opt_tex_ref triangle_at(int frame);
opt_tex_ref highlight_at(int frame);
std::optional<std::reference_wrapper<sf::Texture>> opt_tex_ref outline_at(int frame);
getSquareHighlightTexture(float seconds); opt_tex_ref background_at(int frame);
std::optional<std::reference_wrapper<sf::Texture>> getSquareOutlineTexture(float seconds); opt_tex_ref tail_at(int frame);
std::optional<std::reference_wrapper<sf::Texture>>
getSquareBackgroundTexture(float seconds);
std::optional<std::reference_wrapper<sf::Texture>> getTailTexture(float seconds);
private: private:
std::array<sf::Texture, 16> triangle_appearance; std::array<sf::Texture, 16> triangle_appearance;
@ -45,7 +43,8 @@ private:
const std::filesystem::path& folder, const std::filesystem::path& folder,
const std::string& prefix, const std::string& prefix,
int left_padding = 3, int left_padding = 3,
const std::string& extension = ".png") { const std::string& extension = ".png"
) {
std::array<sf::Texture, number> res; std::array<sf::Texture, number> res;
for (int frame = first; frame <= first + number - 1; frame++) { for (int frame = first; frame <= first + number - 1; frame++) {
std::stringstream filename; std::stringstream filename;

View File

@ -177,14 +177,14 @@ int main(int argc, char** argv) {
// current time // current time
editor_state->chart->timeSelection = editor_state->chart->timeSelection =
static_cast<unsigned int>( static_cast<unsigned int>(
editor_state->getCurrentTick()); editor_state->current_tick());
// if the start of the timeSelection is // if the start of the timeSelection is
// already set // already set
} else if (std::holds_alternative<unsigned int>( } else if (std::holds_alternative<unsigned int>(
editor_state->chart->timeSelection)) { editor_state->chart->timeSelection)) {
auto current_tick = auto current_tick =
static_cast<int>(editor_state->getCurrentTick()); static_cast<int>(editor_state->current_tick());
auto selection_start = auto selection_start =
static_cast<int>(std::get<unsigned int>( static_cast<int>(std::get<unsigned int>(
editor_state->chart->timeSelection)); editor_state->chart->timeSelection));
@ -219,7 +219,7 @@ int main(int argc, char** argv) {
// current time // current time
editor_state->chart->timeSelection = editor_state->chart->timeSelection =
static_cast<unsigned int>( static_cast<unsigned int>(
editor_state->getCurrentTick()); editor_state->current_tick());
} }
} }
break; break;
@ -443,9 +443,9 @@ int main(int argc, char** argv) {
} }
} }
if (beatTick.shouldPlay) { if (beatTick.shouldPlay) {
auto previous_tick = static_cast<int>(editor_state->getTicksAt( auto previous_tick = static_cast<int>(editor_state->ticks_at(
editor_state->previousPos.asSeconds())); editor_state->previousPos.asSeconds()));
auto current_tick = static_cast<int>(editor_state->getTicksAt( auto current_tick = static_cast<int>(editor_state->ticks_at(
editor_state->playbackPosition.asSeconds())); editor_state->playbackPosition.asSeconds()));
if (previous_tick / editor_state->getResolution() if (previous_tick / editor_state->getResolution()
!= current_tick / editor_state->getResolution()) { != current_tick / editor_state->getResolution()) {

19
src/time_interval.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "time_interval.hpp"
#include <algorithm>
TimeInterval::TimeInterval(const sf::Time& start, const sf::Time& end) :
start(std::min(start, end)),
end(std::max(start, end))
{};
// interval union
TimeInterval& TimeInterval::operator+=(const TimeInterval& rhs) {
start = std::min(start, rhs.start);
end = std::max(end, rhs.end);
return *this;
};
TimeInterval& TimeInterval::operator+=(const sf::Time& rhs) {
return this->operator+=(TimeInterval{rhs, rhs});
};

26
src/time_interval.hpp Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <compare>
#include <SFML/System/Time.hpp>
class TimeInterval {
public:
TimeInterval() = default;
TimeInterval(const sf::Time& start, const sf::Time& end);
bool operator==(const TimeInterval&) const = default;
TimeInterval& operator+=(const TimeInterval& rhs);
TimeInterval& operator+=(const sf::Time& rhs);
// passing lhs by value helps optimize chained a+b+c (says cppreference)
template<typename T>
friend TimeInterval operator+(TimeInterval lhs, const T& rhs) {
lhs += rhs;
return lhs;
};
sf::Time start;
sf::Time end;
};

View File

@ -143,7 +143,7 @@ void LinearView::update(
PixelsToSeconds.transform(static_cast<float>(y)) + 0.5f))); PixelsToSeconds.transform(static_cast<float>(y)) + 0.5f)));
auto notes = chart->chart.getVisibleNotesBetween(lower_bound_ticks, upper_bound_ticks); auto notes = chart->chart.getVisibleNotesBetween(lower_bound_ticks, upper_bound_ticks);
auto currentLongNote = chart->makeCurrentLongNote(); auto currentLongNote = chart->make_current_long_note();
if (currentLongNote) { if (currentLongNote) {
notes.insert(*currentLongNote); notes.insert(*currentLongNote);
} }

View File

@ -5,7 +5,7 @@
const std::string texture_file = "textures/edit_textures/game_front_edit_tex_1.tex.png"; const std::string texture_file = "textures/edit_textures/game_front_edit_tex_1.tex.png";
Playfield::Playfield(std::filesystem::path assets_folder) : Playfield::Playfield(std::filesystem::path assets_folder) :
longNoteMarker(assets_folder / "textures" / "long"), long_note(assets_folder / "textures" / "long"),
texture_path(assets_folder / texture_file) texture_path(assets_folder / texture_file)
{ {
if (!base_texture.loadFromFile(texture_path)) { if (!base_texture.loadFromFile(texture_path)) {
@ -26,161 +26,156 @@ Playfield::Playfield(std::filesystem::path assets_folder) :
note_collision.setTexture(base_texture); note_collision.setTexture(base_texture);
note_collision.setTextureRect({576, 0, 192, 192}); note_collision.setTextureRect({576, 0, 192, 192});
if (!markerLayer.create(400, 400)) { if (!marker_layer.create(400, 400)) {
std::cerr << "Unable to create Playfield's markerLayer"; std::cerr << "Unable to create Playfield's markerLayer";
throw std::runtime_error("Unable to create Playfield's markerLayer"); throw std::runtime_error("Unable to create Playfield's markerLayer");
} }
markerLayer.setSmooth(true); marker_layer.setSmooth(true);
if (!longNoteLayer.create(400, 400)) { if (!long_note.layer.create(400, 400)) {
std::cerr << "Unable to create Playfield's longNoteLayer"; std::cerr << "Unable to create Playfield's longNoteLayer";
throw std::runtime_error("Unable to create Playfield's longNoteLayer"); throw std::runtime_error("Unable to create Playfield's longNoteLayer");
} }
longNoteLayer.setSmooth(true); long_note.layer.setSmooth(true);
LNSquareBackgroud.setTexture(*longNoteMarker.getSquareBackgroundTexture(0)); long_note.backgroud.setTexture(*long_note.marker.background_at(0));
LNSquareOutline.setTexture(*longNoteMarker.getSquareOutlineTexture(0)); long_note.outline.setTexture(*long_note.marker.outline_at(0));
LNSquareHighlight.setTexture(*longNoteMarker.getSquareHighlightTexture(0)); long_note.highlight.setTexture(*long_note.marker.highlight_at(0));
LNTail.setTexture(*longNoteMarker.getTailTexture(0)); long_note.tail.setTexture(*long_note.marker.tail_at(0));
LNTriangle.setTexture(*longNoteMarker.getTriangleTexture(0)); long_note.triangle.setTexture(*long_note.marker.triangle_at(0));
} }
void Playfield::resize(unsigned int width) { void Playfield::resize(unsigned int width) {
if (longNoteLayer.getSize() != sf::Vector2u(width, width)) { if (long_note.layer.getSize() != sf::Vector2u(width, width)) {
if (!longNoteLayer.create(width, width)) { if (!long_note.layer.create(width, width)) {
std::cerr << "Unable to resize Playfield's longNoteLayer"; std::cerr << "Unable to resize Playfield's longNoteLayer";
throw std::runtime_error( throw std::runtime_error(
"Unable to resize Playfield's longNoteLayer"); "Unable to resize Playfield's longNoteLayer");
} }
longNoteLayer.setSmooth(true); long_note.layer.setSmooth(true);
} }
longNoteLayer.clear(sf::Color::Transparent); long_note.layer.clear(sf::Color::Transparent);
if (markerLayer.getSize() != sf::Vector2u(width, width)) { if (marker_layer.getSize() != sf::Vector2u(width, width)) {
if (!markerLayer.create(width, width)) { if (!marker_layer.create(width, width)) {
std::cerr << "Unable to resize Playfield's markerLayer"; std::cerr << "Unable to resize Playfield's markerLayer";
throw std::runtime_error( throw std::runtime_error(
"Unable to resize Playfield's markerLayer"); "Unable to resize Playfield's markerLayer");
} }
markerLayer.setSmooth(true); marker_layer.setSmooth(true);
} }
markerLayer.clear(sf::Color::Transparent); marker_layer.clear(sf::Color::Transparent);
} }
void Playfield::drawLongNote( void Playfield::draw_long_note(
const Note& note, const better::LongNote& note,
const sf::Time& playbackPosition, const sf::Time& playbackPosition,
const float& ticksAtPlaybackPosition, const better::Timing& timing
const float& BPM, ) {
const int& resolution) { float squareSize = static_cast<float>(long_note.layer.getSize().x) / 4;
float squareSize = static_cast<float>(longNoteLayer.getSize().x) / 4; auto note_time = timing.time_at(note.get_time());
auto note_offset = playbackPosition - note_time;
auto frame = static_cast<int>(std::floor(note_offset.asSeconds() * 30.f));
const auto x = note.get_position().get_x();
const auto y = note.get_position().get_y();
AffineTransform<float> SecondsToTicksProportional(0.f, (60.f / BPM), 0.f, resolution); auto tail_end = timing.time_at(note.get_end());
AffineTransform<float> SecondsToTicks(
playbackPosition.asSeconds() - (60.f / BPM),
playbackPosition.asSeconds(),
ticksAtPlaybackPosition - resolution,
ticksAtPlaybackPosition);
float note_offset = SecondsToTicksProportional.backwards_transform( if (playbackPosition < tail_end) {
ticksAtPlaybackPosition - note.getTiming());
auto frame = static_cast<long long int>(std::floor(note_offset * 30.f));
int x = note.getPos() % 4;
int y = note.getPos() / 4;
float tail_end_in_seconds =
SecondsToTicks.backwards_transform(note.getTiming() + note.getLength());
// float tail_end_offset = playbackPosition.asSeconds() -
// tail_end_in_seconds;
if (playbackPosition.asSeconds() < tail_end_in_seconds) {
// Before or During the long note // Before or During the long note
auto tail_tex = longNoteMarker.getTailTexture(note_offset); if (auto tail_tex = long_note.marker.tail_at(frame)) {
if (tail_tex) {
auto triangle_distance = static_cast<float>((note.getTail_pos() / 4) + 1);
AffineTransform<float> OffsetToTriangleDistance( AffineTransform<float> OffsetToTriangleDistance(
0.f, 0.f,
SecondsToTicksProportional.backwards_transform(note.getLength()), (tail_end - note_time).asSeconds(),
triangle_distance, static_cast<float>(note.get_tail_length()),
0.f); 0.f
);
LNTail.setTexture(*tail_tex, true);
auto LNTriangle_tex = longNoteMarker.getTriangleTexture(note_offset); long_note.tail.setTexture(*tail_tex, true);
if (LNTriangle_tex) { if (auto tex = long_note.marker.triangle_at(frame)) {
LNTriangle.setTexture(*LNTriangle_tex, true); long_note.triangle.setTexture(*tex, true);
} }
auto LNSquareBackgroud_tex = if (auto tex = long_note.marker.background_at(frame)) {
longNoteMarker.getSquareBackgroundTexture(note_offset); long_note.backgroud.setTexture(*tex, true);
if (LNSquareBackgroud_tex) {
LNSquareBackgroud.setTexture(*LNSquareBackgroud_tex, true);
} }
auto LNSquareOutline_tex = longNoteMarker.getSquareOutlineTexture(note_offset); if (auto tex = long_note.marker.outline_at(frame)) {
if (LNSquareOutline_tex) { long_note.outline.setTexture(*tex, true);
LNSquareOutline.setTexture(*LNSquareOutline_tex, true);
} }
auto LNSquareHighlight_tex = if (auto tex = long_note.marker.highlight_at(frame)) {
longNoteMarker.getSquareHighlightTexture(note_offset); long_note.highlight.setTexture(*tex, true);
if (LNSquareHighlight_tex) {
LNSquareHighlight.setTexture(*LNSquareHighlight_tex, true);
} }
auto rect = LNTail.getTextureRect(); auto rect = long_note.tail.getTextureRect();
float tail_length_factor; float tail_length_factor;
if (frame < 8) { if (frame < 8) {
// Before the note : tail goes from triangle tip to note edge // Before the note : tail goes from triangle tip to note edge
tail_length_factor = tail_length_factor = std::max(
std::max(0.f, OffsetToTriangleDistance.clampedTransform(note_offset) - 1.f); 0.f,
OffsetToTriangleDistance.clampedTransform(
note_offset.asSeconds()
) - 1.f
);
} else { } else {
// During the note : tail goes from half of the triangle base to // During the note : tail goes from half of the triangle base to
// note edge // note edge
tail_length_factor = tail_length_factor = std::max(
std::max(0.f, OffsetToTriangleDistance.clampedTransform(note_offset) - 0.5f); 0.f,
OffsetToTriangleDistance.clampedTransform(
note_offset.asSeconds()
) - 0.5f
);
} }
rect.height = static_cast<int>(rect.height * tail_length_factor); rect.height = static_cast<int>(rect.height * tail_length_factor);
LNTail.setTextureRect(rect); long_note.tail.setTextureRect(rect);
LNTail.setOrigin(rect.width / 2.f, -rect.width / 2.f); long_note.tail.setOrigin(rect.width / 2.f, -rect.width / 2.f);
LNTail.setRotation(90.f * ((note.getTail_pos() + 2) % 4)); long_note.tail.setRotation(note.get_tail_angle() + 180);
rect = LNTriangle.getTextureRect(); rect = long_note.triangle.getTextureRect();
LNTriangle.setOrigin( long_note.triangle.setOrigin(
rect.width / 2.f, rect.width / 2.f,
rect.width * (0.5f + OffsetToTriangleDistance.clampedTransform(note_offset))); rect.width * (
LNTriangle.setRotation(90.f * (note.getTail_pos() % 4)); 0.5f
+ OffsetToTriangleDistance.clampedTransform(
note_offset.asSeconds()
)
)
);
long_note.triangle.setRotation(note.get_tail_angle());
rect = LNSquareBackgroud.getTextureRect(); rect = long_note.backgroud.getTextureRect();
LNSquareBackgroud.setOrigin(rect.width / 2.f, rect.height / 2.f); long_note.backgroud.setOrigin(rect.width / 2.f, rect.height / 2.f);
LNSquareBackgroud.setRotation(90.f * (note.getTail_pos() % 4)); long_note.backgroud.setRotation(note.get_tail_angle());
rect = LNSquareOutline.getTextureRect(); rect = long_note.outline.getTextureRect();
LNSquareOutline.setOrigin(rect.width / 2.f, rect.height / 2.f); long_note.outline.setOrigin(rect.width / 2.f, rect.height / 2.f);
LNSquareOutline.setRotation(90.f * (note.getTail_pos() % 4)); long_note.outline.setRotation(note.get_tail_angle());
rect = LNSquareHighlight.getTextureRect(); rect = long_note.highlight.getTextureRect();
LNSquareHighlight.setOrigin(rect.width / 2.f, rect.height / 2.f); long_note.highlight.setOrigin(rect.width / 2.f, rect.height / 2.f);
float scale = squareSize / rect.width; float scale = squareSize / rect.width;
LNTail.setScale(scale, scale); long_note.tail.setScale(scale, scale);
LNTriangle.setScale(scale, scale); long_note.triangle.setScale(scale, scale);
LNSquareBackgroud.setScale(scale, scale); long_note.backgroud.setScale(scale, scale);
LNSquareOutline.setScale(scale, scale); long_note.outline.setScale(scale, scale);
LNSquareHighlight.setScale(scale, scale); long_note.highlight.setScale(scale, scale);
LNTail.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize); long_note.tail.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize);
LNTriangle.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize); long_note.triangle.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize);
LNSquareBackgroud.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize); long_note.backgroud.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize);
LNSquareOutline.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize); long_note.outline.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize);
LNSquareHighlight.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize); long_note.highlight.setPosition((x + 0.5f) * squareSize, (y + 0.5f) * squareSize);
longNoteLayer.draw(LNTail); long_note.layer.draw(long_note.tail);
longNoteLayer.draw(LNSquareBackgroud); long_note.layer.draw(long_note.backgroud);
longNoteLayer.draw(LNSquareOutline); long_note.layer.draw(long_note.outline);
longNoteLayer.draw(LNTriangle); long_note.layer.draw(long_note.triangle);
longNoteLayer.draw(LNSquareHighlight); long_note.layer.draw(long_note.highlight);
} }
} }
} }
@ -193,9 +188,9 @@ void Playfield::drawLongNote(
const int& resolution, const int& resolution,
Marker& marker, Marker& marker,
MarkerEndingState& markerEndingState) { MarkerEndingState& markerEndingState) {
drawLongNote(note, playbackPosition, ticksAtPlaybackPosition, BPM, resolution); draw_long_note(note, playbackPosition, ticksAtPlaybackPosition, BPM, resolution);
float squareSize = static_cast<float>(longNoteLayer.getSize().x) / 4; float squareSize = static_cast<float>(long_note.layer.getSize().x) / 4;
AffineTransform<float> SecondsToTicksProportional(0.f, (60.f / BPM), 0.f, resolution); AffineTransform<float> SecondsToTicksProportional(0.f, (60.f / BPM), 0.f, resolution);
AffineTransform<float> SecondsToTicks( AffineTransform<float> SecondsToTicks(
@ -219,10 +214,10 @@ void Playfield::drawLongNote(
auto t = marker.getSprite(markerEndingState, note_offset); auto t = marker.getSprite(markerEndingState, note_offset);
if (t) { if (t) {
float scale = squareSize / t->get().getSize().x; float scale = squareSize / t->get().getSize().x;
markerSprite.setTexture(*t, true); marker_sprite.setTexture(*t, true);
markerSprite.setScale(scale, scale); marker_sprite.setScale(scale, scale);
markerSprite.setPosition(x * squareSize, y * squareSize); marker_sprite.setPosition(x * squareSize, y * squareSize);
markerLayer.draw(markerSprite); marker_layer.draw(markerSprite);
} }
} else { } else {
@ -231,10 +226,10 @@ void Playfield::drawLongNote(
auto t = marker.getSprite(markerEndingState, tail_end_offset); auto t = marker.getSprite(markerEndingState, tail_end_offset);
if (t) { if (t) {
float scale = squareSize / t->get().getSize().x; float scale = squareSize / t->get().getSize().x;
markerSprite.setTexture(*t, true); marker_sprite.setTexture(*t, true);
markerSprite.setScale(scale, scale); marker_sprite.setScale(scale, scale);
markerSprite.setPosition(x * squareSize, y * squareSize); marker_sprite.setPosition(x * squareSize, y * squareSize);
markerLayer.draw(markerSprite); marker_layer.draw(markerSprite);
} }
} }
} }

View File

@ -1,9 +1,12 @@
#pragma once #pragma once
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include <SFML/Graphics/RenderTexture.hpp>
#include <imgui-SFML.h> #include <imgui-SFML.h>
#include <string> #include <string>
#include "../better_note.hpp"
#include "../better_timing.hpp"
#include "../ln_marker.hpp" #include "../ln_marker.hpp"
#include "../marker.hpp" #include "../marker.hpp"
#include "../note.hpp" #include "../note.hpp"
@ -17,34 +20,39 @@ public:
sf::Sprite note_selected; sf::Sprite note_selected;
sf::Sprite note_collision; sf::Sprite note_collision;
sf::RenderTexture markerLayer; sf::RenderTexture marker_layer;
sf::Sprite markerSprite; sf::Sprite marker_sprite;
LNMarker longNoteMarker; struct LongNote {
sf::RenderTexture longNoteLayer; template<class... Args>
sf::Sprite LNSquareBackgroud; LongNote(Args&& ...args) : marker(std::forward<Args>(args)...) {};
sf::Sprite LNSquareOutline;
sf::Sprite LNSquareHighlight; LNMarker marker;
sf::Sprite LNTail; sf::RenderTexture layer;
sf::Sprite LNTriangle; sf::Sprite backgroud;
sf::Sprite outline;
sf::Sprite highlight;
sf::Sprite tail;
sf::Sprite triangle;
};
LongNote long_note;
void resize(unsigned int width); void resize(unsigned int width);
void drawLongNote( void draw_long_note(
const Note& note, const better::LongNote& note,
const sf::Time& playbackPosition, const sf::Time& playbackPosition,
const float& ticksAtPlaybackPosition, const better::Timing& timing
const float& BPM, );
const int& resolution);
void drawLongNote( void drawLongNote(
const Note& note, const Note& note,
const sf::Time& playbackPosition, const sf::Time& playbackPosition,
const float& ticksAtPlaybackPosition, const better::Timing& timing,
const float& BPM,
const int& resolution,
Marker& marker, Marker& marker,
MarkerEndingState& markerEndingState); MarkerEndingState& markerEndingState
);
private: private:
const std::filesystem::path texture_path; const std::filesystem::path texture_path;