ok it look slike offset is fixed for now !

This commit is contained in:
Stepland 2022-11-10 19:28:25 +01:00
parent 69e8add495
commit de72d441ae
9 changed files with 82 additions and 32 deletions

View File

@ -8,6 +8,17 @@
#include "json_decimal_handling.hpp" #include "json_decimal_handling.hpp"
namespace better { namespace better {
bool Chart::operator==(const Chart& other) const {
return (
level == other.level
and (
((not timing.has_value()) and (not other.timing.has_value()))
or (**timing == **other.timing)
) and hakus == other.hakus
and *notes == *other.notes
);
}
nlohmann::ordered_json Chart::dump_to_memon_1_0_0( nlohmann::ordered_json Chart::dump_to_memon_1_0_0(
const nlohmann::ordered_json& fallback_timing_object const nlohmann::ordered_json& fallback_timing_object
) const { ) const {

View File

@ -20,7 +20,7 @@ namespace better {
std::optional<Hakus> hakus; std::optional<Hakus> hakus;
std::shared_ptr<Notes> notes = std::make_shared<Notes>(); std::shared_ptr<Notes> notes = std::make_shared<Notes>();
bool operator==(const Chart&) const = default; bool operator==(const Chart&) const;
nlohmann::ordered_json dump_to_memon_1_0_0( nlohmann::ordered_json dump_to_memon_1_0_0(
const nlohmann::ordered_json& fallback_timing_object const nlohmann::ordered_json& fallback_timing_object

View File

@ -1,5 +1,6 @@
#include "better_song.hpp" #include "better_song.hpp"
#include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <fmt/core.h> #include <fmt/core.h>
@ -193,6 +194,15 @@ namespace better {
return song; return song;
}; };
bool Song::operator==(const Song& other) const {
return (
charts == other.charts
and metadata == other.metadata
and *timing == *other.timing
and hakus == other.hakus
);
}
std::ostream& operator<<(std::ostream& out, const Song& s) { std::ostream& operator<<(std::ostream& out, const Song& s) {
out << fmt::format("{}", s); out << fmt::format("{}", s);
return out; return out;

View File

@ -77,7 +77,7 @@ namespace better {
*/ */
static Song load_from_memon_legacy(const nlohmann::json& memon); static Song load_from_memon_legacy(const nlohmann::json& memon);
bool operator==(const Song&) const = default; bool operator==(const Song&) const;
friend std::ostream& operator<<(std::ostream& out, const Song& s); friend std::ostream& operator<<(std::ostream& out, const Song& s);
}; };

View File

@ -89,15 +89,16 @@ namespace better {
change change
*/ */
double Timing::seconds_at(Fraction beats) const { double Timing::seconds_at(Fraction beats) const {
return offset_as_double + seconds_without_offset_at(beats);
};
double Timing::seconds_without_offset_at(Fraction beats) const {
const auto& bpm_change = bpm_event_in_effect_at(beats); const auto& bpm_change = bpm_event_in_effect_at(beats);
const Fraction beats_since_previous_event = beats - bpm_change.get_beats(); const Fraction beats_since_previous_event = beats - bpm_change.get_beats();
double seconds_since_previous_event = static_cast<double>(beats_since_previous_event) * 60 / bpm_change.get_bpm_as_double(); double seconds_since_previous_event = static_cast<double>(beats_since_previous_event) * 60 / bpm_change.get_bpm_as_double();
return ( const auto previous_event_seconds = bpm_change.get_seconds();
offset_as_double return previous_event_seconds + seconds_since_previous_event;
+ bpm_change.get_seconds() }
+ seconds_since_previous_event
);
};
double Timing::seconds_between(Fraction beat_a, Fraction beat_b) const { double Timing::seconds_between(Fraction beat_a, Fraction beat_b) const {
return seconds_at(beat_b) - seconds_at(beat_a); return seconds_at(beat_b) - seconds_at(beat_a);
@ -118,8 +119,9 @@ namespace better {
Fraction Timing::beats_at(double seconds) const { Fraction Timing::beats_at(double seconds) const {
const auto& bpm_change = bpm_event_in_effect_at(seconds); const auto& bpm_change = bpm_event_in_effect_at(seconds);
auto seconds_since_previous_event = seconds - bpm_change.get_seconds(); const auto previous_event_seconds = bpm_change.get_seconds() + offset_as_double;
auto beats_since_previous_event = ( const auto seconds_since_previous_event = seconds - previous_event_seconds;
const auto beats_since_previous_event = (
convert_to_fraction(bpm_change.get_bpm()) convert_to_fraction(bpm_change.get_bpm())
* Fraction{seconds_since_previous_event} * Fraction{seconds_since_previous_event}
/ 60 / 60
@ -182,7 +184,8 @@ namespace better {
} }
void Timing::set_offset(const Decimal& new_offset) { void Timing::set_offset(const Decimal& new_offset) {
shift_to_match(new_offset); offset = new_offset;
offset_as_double = std::stod(new_offset.format("f"));
} }
@ -242,7 +245,7 @@ namespace better {
void Timing::reconstruct(const std::vector<BPMAtBeat>& events, const Decimal& offset) { void Timing::reconstruct(const std::vector<BPMAtBeat>& events, const Decimal& offset) {
reload_events_from(events); reload_events_from(events);
shift_to_match(offset); set_offset(offset);
} }
void Timing::reload_events_from(const std::vector<BPMAtBeat>& events) { void Timing::reload_events_from(const std::vector<BPMAtBeat>& events) {
@ -274,9 +277,8 @@ namespace better {
} }
} }
// Compute seconds offsets as if the first event happened at second 0 // Compute everything as if the first BPM change happened at zero
// then shift // seconds
// Bootstrap the alg by computing the first one out of the loop
auto first_event = filtered_events.begin(); auto first_event = filtered_events.begin();
double current_second = 0; double current_second = 0;
events_by_beats.clear(); events_by_beats.clear();
@ -298,19 +300,17 @@ namespace better {
current->get_bpm() current->get_bpm()
); );
} }
}
void Timing::shift_to_match(const Decimal& offset_) { // Shift events so their precomputed "seconds" put beat zero
offset = offset_; // at zero seconds
offset_as_double = std::stod(offset_.format("f")); const auto shift = seconds_without_offset_at(0);
const auto shift = offset_as_double - seconds_at(0);
Timing::keys_by_beats_type shifted_events; Timing::keys_by_beats_type shifted_events;
seconds_to_beats.clear(); seconds_to_beats.clear();
std::for_each( std::for_each(
events_by_beats.cbegin(), events_by_beats.cbegin(),
events_by_beats.cend(), events_by_beats.cend(),
[&](const auto& event) { [&](const auto& event) {
const auto seconds = event.get_seconds() + shift; const auto seconds = event.get_seconds() - shift;
const auto beats = event.get_beats(); const auto beats = event.get_beats();
shifted_events.emplace(beats, seconds, event.get_bpm()); shifted_events.emplace(beats, seconds, event.get_bpm());
seconds_to_beats.emplace(seconds, beats); seconds_to_beats.emplace(seconds, beats);
@ -325,7 +325,7 @@ namespace better {
} }
const Timing::key_type& Timing::bpm_event_in_effect_at(double seconds) const { const Timing::key_type& Timing::bpm_event_in_effect_at(double seconds) const {
auto it = seconds_to_beats.upper_bound(seconds); auto it = seconds_to_beats.upper_bound(seconds - offset_as_double);
if (it != seconds_to_beats.begin()) { if (it != seconds_to_beats.begin()) {
it = std::prev(it); it = std::prev(it);
} }

View File

@ -69,6 +69,9 @@ namespace better {
Timing(const std::vector<BPMAtBeat>& events, const Decimal& offset); Timing(const std::vector<BPMAtBeat>& events, const Decimal& offset);
double seconds_at(Fraction beats) const; double seconds_at(Fraction beats) const;
private:
double seconds_without_offset_at(Fraction beats) const;
public:
double seconds_between(Fraction beat_a, Fraction beat_b) const; double 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; sf::Time time_between(Fraction beat_a, Fraction beat_b) const;
@ -106,14 +109,17 @@ namespace better {
Decimal offset = 0; Decimal offset = 0;
double offset_as_double = 0; double offset_as_double = 0;
// These containers hold shared pointers to the same objects // holds the bpm changes with seconds precomputed, seconds are synced
// as if beat zero was happening at zero seconds, ignoring any offset
keys_by_beats_type events_by_beats = {}; keys_by_beats_type events_by_beats = {};
// holds a <seconds, beats> pair for each event to allow a quick search
// when querying by seconds, seconds held in this are synced as if beat
// zero was happenning at zero seconds
std::map<double, Fraction> seconds_to_beats = {}; std::map<double, Fraction> seconds_to_beats = {};
void reconstruct(const std::vector<BPMAtBeat>& events, const Decimal& offset); void reconstruct(const std::vector<BPMAtBeat>& events, const Decimal& offset);
/* Reload the timing object assuming the first event in the given /* Reload using the given events */
vector happens at second zero */
void reload_events_from(const std::vector<BPMAtBeat>& events); void reload_events_from(const std::vector<BPMAtBeat>& events);
/* Shift all events in the timing object to make beat zero happen /* Shift all events in the timing object to make beat zero happen

View File

@ -10,7 +10,7 @@ TEST_CASE("make_long_note works with any input") {
better::Position pos_b{index_b}; better::Position pos_b{index_b};
better::TapNote a{0, pos_a}; better::TapNote a{0, pos_a};
better::TapNote b{0, pos_b}; better::TapNote b{0, pos_b};
REQUIRE_NOTHROW(make_linear_view_long_note_dummy({a, b}, 1)); REQUIRE_NOTHROW(make_long_note_dummy_for_linear_view({a, b}, 1));
} }
} }
} }

View File

@ -20,6 +20,7 @@
#include "../../better_song.hpp" #include "../../better_song.hpp"
#include "../../better_timing.hpp" #include "../../better_timing.hpp"
#include "../../variant_visitor.hpp" #include "../../variant_visitor.hpp"
#include "rapidcheck/gen/Arbitrary.hpp"
namespace rc { namespace rc {
@ -153,10 +154,15 @@ namespace rc {
} }
}; };
struct TimingParams {
std::vector<better::BPMAtBeat> events;
Decimal offset;
};
template<> template<>
struct Arbitrary<better::Timing> { struct Arbitrary<TimingParams> {
static Gen<better::Timing> arbitrary() { static Gen<TimingParams> arbitrary() {
return gen::construct<better::Timing>( return gen::construct<TimingParams>(
gen::withSize([](std::size_t size) { gen::withSize([](std::size_t size) {
return gen::uniqueBy<std::vector<better::BPMAtBeat>>( return gen::uniqueBy<std::vector<better::BPMAtBeat>>(
std::max(std::size_t(1), size), std::max(std::size_t(1), size),
@ -169,6 +175,18 @@ namespace rc {
} }
}; };
template<>
struct Arbitrary<better::Timing> {
static Gen<better::Timing> arbitrary() {
return gen::map(
gen::arbitrary<TimingParams>(),
[](const TimingParams& t){
return better::Timing{t.events, t.offset};
}
);
}
};
template<> template<>
struct Arbitrary<Hakus> { struct Arbitrary<Hakus> {
static Gen<Hakus> arbitrary() { static Gen<Hakus> arbitrary() {
@ -197,9 +215,13 @@ namespace rc {
static Gen<better::Chart> arbitrary() { static Gen<better::Chart> arbitrary() {
return gen::construct<better::Chart>( return gen::construct<better::Chart>(
gen::arbitrary<std::optional<Decimal>>(), gen::arbitrary<std::optional<Decimal>>(),
gen::construct<std::optional<better::Timing>>(gen::arbitrary<better::Timing>()), gen::construct<std::optional<std::shared_ptr<better::Timing>>>(
gen::makeShared<better::Timing>(gen::arbitrary<better::Timing>())
),
gen::arbitrary<std::optional<Hakus>>(), gen::arbitrary<std::optional<Hakus>>(),
gen::arbitrary<better::Notes>() gen::construct<std::shared_ptr<better::Notes>>(
gen::makeShared<better::Notes>(gen::arbitrary<better::Notes>())
)
); );
} }
}; };
@ -254,7 +276,7 @@ namespace rc {
return gen::construct<better::Song>( return gen::construct<better::Song>(
gen::arbitrary<std::map<std::string, better::Chart, better::OrderByDifficultyName>>(), gen::arbitrary<std::map<std::string, better::Chart, better::OrderByDifficultyName>>(),
gen::arbitrary<better::Metadata>(), gen::arbitrary<better::Metadata>(),
gen::arbitrary<better::Timing>(), gen::makeShared<better::Timing>(gen::arbitrary<better::Timing>()),
gen::arbitrary<std::optional<Hakus>>() gen::arbitrary<std::optional<Hakus>>()
); );
} }

View File

@ -312,6 +312,7 @@ void LinearView::draw(
ImGui::IsMouseClicked(ImGuiMouseButton_Left) ImGui::IsMouseClicked(ImGuiMouseButton_Left)
and current_window->InnerClipRect.Contains(ImGui::GetMousePos()) and current_window->InnerClipRect.Contains(ImGui::GetMousePos())
and not ImGui::IsAnyItemHovered() and not ImGui::IsAnyItemHovered()
and ImGui::IsWindowFocused()
) { ) {
started_selection_inside_window = true; started_selection_inside_window = true;
} }