diff --git a/src/better_beats.cpp b/src/better_beats.cpp index 4d71968..e707cd4 100644 --- a/src/better_beats.cpp +++ b/src/better_beats.cpp @@ -14,9 +14,7 @@ bool is_expressible_as_240th(const Fraction& beat) { nlohmann::ordered_json beat_to_best_form(const Fraction& beat) { if (is_expressible_as_240th(beat)) { - return nlohmann::ordered_json( - (240 * convert_to_u64(beat.numerator())) / convert_to_u64(beat.denominator()) - ); + return nlohmann::ordered_json(static_cast(240 * beat)); } else { return beat_to_fraction_tuple(beat); }; diff --git a/src/better_chart.hpp b/src/better_chart.hpp index d2f02ff..983ec99 100644 --- a/src/better_chart.hpp +++ b/src/better_chart.hpp @@ -18,6 +18,8 @@ namespace better { std::optional hakus; Notes notes; + bool operator==(const Chart&) const = default; + nlohmann::ordered_json dump_to_memon_1_0_0( const nlohmann::ordered_json& fallback_timing_object ) const; diff --git a/src/better_note.cpp b/src/better_note.cpp index 80a18d9..45b8428 100644 --- a/src/better_note.cpp +++ b/src/better_note.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "better_beats.hpp" @@ -16,7 +17,7 @@ namespace better { }; Position::Position(std::uint64_t x, std::uint64_t y) : x(x), y(y) { - if (x > 3 or y > 2) { + if (x > 3 or y > 3) { std::stringstream ss; ss << "Attempted to create Position from invalid coordinates : "; ss << *this; @@ -37,7 +38,7 @@ namespace better { }; std::ostream& operator<< (std::ostream& out, const Position& pos) { - out << fmt::format("(x: {}, y: {})", pos.x, pos.y); + out << fmt::to_string(pos); return out; }; @@ -52,6 +53,11 @@ namespace better { return position; }; + std::ostream& operator<<(std::ostream& out, const TapNote& t) { + out << fmt::to_string(t); + return out; + }; + nlohmann::ordered_json TapNote::dump_to_memon_1_0_0() const { return { {"n", position.index()}, @@ -142,6 +148,11 @@ namespace better { } }; + std::ostream& operator<<(std::ostream& out, const LongNote& l) { + out << fmt::to_string(l); + return out; + }; + nlohmann::ordered_json LongNote::dump_to_memon_1_0_0() const { return { {"n", position.index()}, @@ -230,6 +241,11 @@ namespace better { return this->get_time_bounds().second; } + std::ostream& operator<<(std::ostream& out, const Note& n) { + out << fmt::to_string(n); + return out; + }; + nlohmann::ordered_json Note::dump_to_memon_1_0_0() const { return std::visit([](const auto& n){return n.dump_to_memon_1_0_0();}, this->note); } @@ -242,7 +258,7 @@ namespace better { } const auto duration = load_memon_1_0_0_beat(json["l"], resolution); - const auto tail_index = json["n"].get(); + const auto tail_index = json["p"].get(); return LongNote{ time, position, @@ -261,7 +277,7 @@ namespace better { json["l"].get(), resolution, }; - const auto tail_index = json["n"].get(); + const auto tail_index = json["p"].get(); if (duration > 0) { return LongNote{ time, diff --git a/src/better_note.hpp b/src/better_note.hpp index 3bf7dc2..30aeb30 100644 --- a/src/better_note.hpp +++ b/src/better_note.hpp @@ -6,6 +6,8 @@ #include #include +#include +#include #include #include "special_numeric_types.hpp" @@ -46,6 +48,7 @@ namespace better { Position get_position() const; bool operator==(const TapNote&) const = default; + friend std::ostream& operator<<(std::ostream& out, const TapNote& pos); nlohmann::ordered_json dump_to_memon_1_0_0() const; private: @@ -66,6 +69,7 @@ namespace better { std::uint64_t get_tail_angle() const; bool operator==(const LongNote&) const = default; + friend std::ostream& operator<<(std::ostream& out, const LongNote& pos); nlohmann::ordered_json dump_to_memon_1_0_0() const; int tail_as_6_notation() const; @@ -92,6 +96,7 @@ namespace better { auto visit(T& visitor) const {return std::visit(visitor, this->note);}; bool operator==(const Note&) const = default; + friend std::ostream& operator<<(std::ostream& out, const Note& pos); nlohmann::ordered_json dump_to_memon_1_0_0() const; @@ -109,3 +114,57 @@ namespace better { }; } +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const better::Position& pos, FormatContext& ctx) { + return format_to( + ctx.out(), + "(x: {}, y: {})", + pos.get_x(), + pos.get_y() + ); + } +}; + +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const better::TapNote& t, FormatContext& ctx) { + return format_to( + ctx.out(), + "TapNote(time: {}, position: {})", + t.get_time(), + t.get_position() + ); + } +}; + +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const better::LongNote& l, FormatContext& ctx) { + return format_to( + ctx.out(), + "LongNote(time: {}, position: {}, duration: {}, tail tip: {})", + l.get_time(), + l.get_position(), + l.get_duration(), + l.get_tail_tip() + ); + } +}; + +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const better::Note& n, FormatContext& ctx) { + const auto visitor = [&](const auto& n){return format_to(ctx.out(), "{}", n);}; + return n.visit(visitor); + } +}; + diff --git a/src/better_notes.cpp b/src/better_notes.cpp index 87c6c0e..82d5c8f 100644 --- a/src/better_notes.cpp +++ b/src/better_notes.cpp @@ -1,38 +1,39 @@ #include "better_notes.hpp" -#include #include #include #include -#include "json.hpp" + +#include +#include namespace better { - std::pair Notes::insert(const Note& note) { - auto conflicting_note = end(); - in( + std::pair Notes::insert(const Note& note) { + auto conflicting_note = notes.end(); + notes.in( note.get_time_bounds(), - [&](const Notes::iterator& it){ + [&](const Notes::container::iterator& it){ if ( it->second.get_position() == note.get_position() - and conflicting_note == end() + and conflicting_note == notes.end() ) { conflicting_note = it; } } ); - if (conflicting_note != end()) { + if (conflicting_note != notes.end()) { return {conflicting_note, false}; } else { - auto it = interval_tree::insert({note.get_time_bounds(), note}); + auto it = notes.insert({note.get_time_bounds(), note}); return {it, true}; } }; void Notes::overwriting_insert(const Note& note) { std::vector conflicting_notes = {}; - in( + notes.in( note.get_time_bounds(), - [&](const Notes::const_iterator& it) { + [&](const Notes::container::const_iterator& it) { if (it->second.get_position() == note.get_position()) { conflicting_notes.push_back(it->second); } @@ -41,15 +42,15 @@ namespace better { for (const auto& conflict : conflicting_notes) { erase(conflict); } - interval_tree::insert({note.get_time_bounds(), note}); + notes.insert({note.get_time_bounds(), note}); }; - Notes::const_iterator Notes::find(const Note& note) const { - auto conflicting_note = interval_tree::end(); - in( + Notes::container::const_iterator Notes::find(const Note& note) const { + auto conflicting_note = notes.end(); + notes.in( note.get_time_bounds(), - [&](Notes::const_iterator it){ - if (it->second == note and conflicting_note == end()) { + [&](Notes::container::const_iterator it){ + if (it->second == note and conflicting_note == notes.end()) { conflicting_note = it; } } @@ -58,12 +59,12 @@ namespace better { }; bool Notes::contains(const Note& note) const { - return find(note) != cend(); + return find(note) != notes.cend(); }; void Notes::erase(const Note& note) { auto it = find(note); - interval_tree::erase(it); + notes.erase(it); }; @@ -91,9 +92,9 @@ namespace better { const auto collision_end = timing.beats_at(timing.time_at(end_beat) + sf::seconds(1)); bool found_collision = false; - in( + notes.in( {collision_start, collision_end}, - [&](const Notes::const_iterator& it){ + [&](const Notes::container::const_iterator& it){ if (it->second.get_position() == note.get_position()) { if (it->second != note) { found_collision = true; @@ -105,24 +106,24 @@ namespace better { }; Notes Notes::between(const Interval& bounds) { - auto its = in(bounds.start, bounds.end); + auto its = notes.in(bounds.start, bounds.end); Notes res; - res.interval_tree::insert(*its.begin(), *its.end()); + res.notes.insert(*its.begin(), *its.end()); return res; }; std::size_t Notes::count_between(const Interval& bounds) { std::size_t count = 0; - in( + notes.in( {bounds.start, bounds.end}, - [&](const Notes::const_iterator& it){count++;} + [&](const Notes::container::const_iterator& it){count++;} ); return count; }; nlohmann::ordered_json Notes::dump_to_memon_1_0_0() const { auto json_notes = nlohmann::ordered_json::array(); - for (const auto& [_, note] : *this) { + for (const auto& [_, note] : notes) { json_notes.push_back(note.dump_to_memon_1_0_0()); } return json_notes; diff --git a/src/better_notes.hpp b/src/better_notes.hpp index 48a6fae..b806b64 100644 --- a/src/better_notes.hpp +++ b/src/better_notes.hpp @@ -15,14 +15,15 @@ #include "special_numeric_types.hpp" namespace better { - class Notes : public interval_tree { + class Notes { public: + using container = interval_tree; // try to insert a note, the boolean is true on success - std::pair insert(const Note& note); + std::pair insert(const Note& note); // insert a note, erasing any other note it collides with void overwriting_insert(const Note& note); // returns at iterator to a note exactly equal, if found - const_iterator find(const Note& note) const; + container::const_iterator find(const Note& note) const; bool contains(const Note& note) const; void erase(const Note& note); @@ -40,5 +41,10 @@ namespace better { static Notes load_from_memon_1_0_0(const nlohmann::json& json, std::uint64_t resolution = 240); static Notes load_from_memon_legacy(const nlohmann::json& json, std::uint64_t resolution); + + bool operator==(const Notes&) const = default; + + private: + interval_tree notes; }; } \ No newline at end of file diff --git a/src/better_timing.hpp b/src/better_timing.hpp index 7f3856e..c95d842 100644 --- a/src/better_timing.hpp +++ b/src/better_timing.hpp @@ -77,6 +77,8 @@ namespace better { static Timing load_from_memon_1_0_0(const nlohmann::json& json); static Timing load_from_memon_legacy(const nlohmann::json& metadata); + bool operator==(const Timing&) const = default; + private: Decimal offset; double offset_as_double; diff --git a/src/editor_state.cpp b/src/editor_state.cpp index 71c6623..be1e687 100644 --- a/src/editor_state.cpp +++ b/src/editor_state.cpp @@ -439,7 +439,7 @@ void EditorState::display_playback_status() { ImGui::SameLine(); ImGui::TextDisabled("Beats :"); ImGui::SameLine(); - ImGui::TextUnformatted(fmt::format("{:.3f}", current_exact_beats().get_d()).c_str()); + ImGui::TextUnformatted(fmt::format("{:.3f}", static_cast(current_exact_beats())).c_str()); ImGui::SameLine(); if (music) { ImGui::TextDisabled("Music File Offset :"); diff --git a/src/notes_clipboard.cpp b/src/notes_clipboard.cpp index 972b874..76ee328 100644 --- a/src/notes_clipboard.cpp +++ b/src/notes_clipboard.cpp @@ -8,7 +8,7 @@ void NotesClipboard::copy(const better::Notes& notes) { contents.clear(); if (not notes.empty()) { const auto offset = notes.cbegin()->second.get_time(); - const auto shift = shifter(-offset); + const auto shift = shifter(-1 * offset); for (const auto& [_, note] : notes) { contents.insert(note.visit(shift)); } diff --git a/src/special_numeric_types.cpp b/src/special_numeric_types.cpp index 6fbbe91..fe1b23e 100644 --- a/src/special_numeric_types.cpp +++ b/src/special_numeric_types.cpp @@ -1,4 +1,7 @@ #include "special_numeric_types.hpp" + +#include +#include #include Fraction::Fraction(const Decimal& d) { @@ -15,15 +18,11 @@ Fraction::Fraction(const Decimal& d) { } Fraction::operator std::int64_t() const { - const auto a = convert_to_i64(value.get_num()); - const auto b = convert_to_i64(value.get_den()); - return a / b; + return convert_to_i64(floor_fraction(*this).numerator()); }; Fraction::operator std::uint64_t() const { - const auto a = convert_to_u64(value.get_num()); - const auto b = convert_to_u64(value.get_den()); - return a / b; + return convert_to_u64(floor_fraction(*this).numerator()); }; Fraction::operator double() const { @@ -190,4 +189,32 @@ Fraction floor_beats(Fraction beats, std::uint64_t denominator) { beats *= denominator; const auto nearest = floor_fraction(beats); return nearest / Fraction{denominator}; -}; \ No newline at end of file +}; + +Decimal convert_to_decimal(const Fraction& f, unsigned int decimal_places) { + const auto abs_f = f > 0 ? f : -1 * f; + std::int64_t precision = 0; + if (abs_f > 1) { + const std::int64_t numerator_digits = mpz_sizeinbase(f.numerator().get_mpz_t(), 10); + const std::int64_t denominator_digits = mpz_sizeinbase(f.denominator().get_mpz_t(), 10); + if (numerator_digits == denominator_digits) { + precision = decimal_places; + } else { + precision = numerator_digits - denominator_digits + decimal_places + 1; + } + + } else { + const auto first_non_zero_place = -static_cast(std::log10(static_cast(abs_f))); + if (decimal_places < first_non_zero_place) { + return Decimal{0}; + } else { + precision = decimal_places - first_non_zero_place; + } + } + decimal::Context c{precision}; + if (f < 0) { + return -1 * Decimal{convert_to_u64((-1 * f).numerator())}.div(convert_to_u64(f.denominator()), c); + } else { + return Decimal{convert_to_u64(f.numerator())}.div(convert_to_u64(f.denominator()), c); + } +} \ No newline at end of file diff --git a/src/special_numeric_types.hpp b/src/special_numeric_types.hpp index fde189d..f8e89cc 100644 --- a/src/special_numeric_types.hpp +++ b/src/special_numeric_types.hpp @@ -42,11 +42,22 @@ public: friend Fraction operator%(Fraction a, const Fraction& b); friend std::strong_ordering operator<=>(const Fraction& lhs, const Fraction& rhs); friend std::ostream& operator<<(std::ostream& os, const Fraction& obj); + friend struct fmt::formatter; private: mpq_class value; }; +template <> +struct fmt::formatter: formatter { + // parse is inherited from formatter. + template + auto format(const Fraction& c, FormatContext& ctx) { + return formatter::format(c.value.get_str(), ctx); + } +}; + + const auto mpz_uint64_max = mpz_class(fmt::format("{}", UINT64_MAX)); const auto mpz_int64_min = mpz_class(fmt::format("{}", INT64_MIN)); const auto mpz_int64_max = mpz_class(fmt::format("{}", INT64_MAX)); @@ -62,6 +73,8 @@ Fraction convert_to_fraction(const Decimal& d); Fraction round_beats(Fraction beats, std::uint64_t denominator = 240); Fraction floor_beats(Fraction beats, std::uint64_t denominator = 240); +Decimal convert_to_decimal(const Fraction& f, unsigned int decimal_places); + // Stolen from : // https://github.com/progrock-libraries/kickstart/blob/master/source/library/kickstart/main_library/core/ns%E2%96%B8language/operations/intpow.hpp#L36 // Essentially this is Horner's rule adapted to calculating a power, so that the diff --git a/src/tests/doctest/decimals.cpp b/src/tests/doctest/decimals.cpp new file mode 100644 index 0000000..cd71c9c --- /dev/null +++ b/src/tests/doctest/decimals.cpp @@ -0,0 +1,26 @@ +#include + +#include "../../special_numeric_types.hpp" + +TEST_CASE("Decimals") { + SUBCASE("can be constructed from") { + SUBCASE("integers, exactly") { + CHECK(Decimal{1} == Decimal{"1"}); + CHECK(Decimal{-2} == Decimal{"-2"}); + } + SUBCASE("strings, exactly") { + CHECK(Decimal{"0.0001"} == Decimal{1} / Decimal{10000}); + } + SUBCASE("fractions, rounded to a given number of decimal places") { + CHECK(convert_to_decimal({1,3}, 3) == Decimal{"0.333"}); + CHECK(convert_to_decimal({-1,3}, 3) == Decimal{"-0.333"}); + CHECK(convert_to_decimal({1,7}, 5) == Decimal{"0.14286"}); + CHECK(convert_to_decimal({123456,1000}, 1) == Decimal{"123.5"}); + CHECK(convert_to_decimal({1234,1000}, 1) == Decimal{"1.2"}); + CHECK(convert_to_decimal({1000,1234}, 1) == Decimal{"0.8"}); + CHECK(convert_to_decimal({1,777}, 1) == Decimal{"0.0"}); + CHECK(convert_to_decimal({1,777}, 5) == Decimal{"0.00129"}); + CHECK(convert_to_decimal({1,777}, 3) == Decimal{"0.001"}); + } + } +} \ No newline at end of file diff --git a/src/tests/doctest/fractions.cpp b/src/tests/doctest/fractions.cpp index 8fc966d..a10796e 100644 --- a/src/tests/doctest/fractions.cpp +++ b/src/tests/doctest/fractions.cpp @@ -19,6 +19,7 @@ TEST_CASE("Fractions") { SUBCASE("can be cast to") { SUBCASE("std::int64_t, returing the integral part") { CHECK(static_cast(Fraction{1,2}) == INT64_C(0)); + CHECK(static_cast(Fraction{2,3}) == INT64_C(0)); CHECK(static_cast(Fraction{-5,2}) == INT64_C(-2)); CHECK(static_cast(Fraction{"-9223372036854775808/1"}) == INT64_MIN); CHECK(static_cast(Fraction{"9223372036854775807/1"}) == INT64_MAX); diff --git a/src/tests/doctest/meson.build b/src/tests/doctest/meson.build index 28a8638..e6d8288 100644 --- a/src/tests/doctest/meson.build +++ b/src/tests/doctest/meson.build @@ -2,6 +2,7 @@ doctest_tests = executable( 'doctest_tests', 'main.cpp', 'fractions.cpp', + 'decimals.cpp', '../../special_numeric_types.cpp', include_sources['fmt'], dependencies: [ diff --git a/src/tests/rapidcheck/generators.cpp b/src/tests/rapidcheck/generators.cpp new file mode 100644 index 0000000..eafd7aa --- /dev/null +++ b/src/tests/rapidcheck/generators.cpp @@ -0,0 +1,15 @@ +#include "generators.hpp" +#include "rapidcheck/gen/Arbitrary.hpp" + +namespace rc { + template <> + Gen gen::positive() { + return gen::apply([](const Fraction& a, unsigned int b, unsigned int c) { + return a + Fraction{std::min(b, c), std::max(b, c)}; + }, + gen::construct(gen::inRange(0,100)), + gen::inRange(1,10), + gen::inRange(1,10) + ); + }; +} \ No newline at end of file diff --git a/src/tests/rapidcheck/generators.hpp b/src/tests/rapidcheck/generators.hpp index ec9cdd2..7f3e621 100644 --- a/src/tests/rapidcheck/generators.hpp +++ b/src/tests/rapidcheck/generators.hpp @@ -1,9 +1,14 @@ +#pragma once + #include #include #include +#include "../../better_note.hpp" #include "../../better_notes.hpp" +#include "../../better_timing.hpp" #include "../../variant_visitor.hpp" +#include "rapidcheck/gen/Exec.h" namespace rc { template<> @@ -18,34 +23,19 @@ namespace rc { template<> struct Arbitrary { static Gen arbitrary() { - return gen::apply([](const Fraction& a, const Fraction& b) { - return a + b; + return gen::apply([](const Fraction& a, unsigned int b, unsigned int c) { + return a + Fraction{std::min(b, c), std::max(b, c)}; }, - gen::cast( - gen::nonNegative() - ), - gen::construct( - gen::nonNegative(), - gen::positive() - ) - ); - } - - static Gen positive() { - return gen::apply([](const Fraction& a, const Fraction& b) { - return a + b; - }, - gen::cast( - gen::nonNegative() - ), - gen::construct( - gen::positive(), - gen::positive() - ) + gen::construct(gen::inRange(0,100)), + gen::inRange(0,10), + gen::inRange(1,10) ); } }; + template <> + Gen gen::positive(); + template<> struct Arbitrary { static Gen arbitrary() { @@ -59,16 +49,29 @@ namespace rc { template<> struct Arbitrary { static Gen arbitrary() { - const auto pos = *gen::arbitrary(); - const auto tail_6_notation = *gen::inRange(0, 6); - const auto tail_pos = better::convert_6_notation_to_position(pos, tail_6_notation); - return gen::construct( + return gen::apply( + []( + const Fraction& time, + const better::Position& position, + const Fraction& duration, + unsigned int tail_6_notation + ){ + const auto tail_tip = better::convert_6_notation_to_position( + position, tail_6_notation + ); + return better::LongNote{ + time, + position, + duration, + tail_tip, + }; + }, gen::arbitrary(), - gen::just(pos), + gen::arbitrary(), gen::positive(), - gen::just(tail_pos) + gen::inRange(0, 6) ); - } + }; }; template<> @@ -84,32 +87,51 @@ namespace rc { template<> struct Arbitrary { static Gen arbitrary() { - const auto raw_note = * - gen::tuple( + return gen::exec([](){ + const auto raw_notes = *gen::container>>(gen::tuple( gen::arbitrary(), gen::positive(), gen::arbitrary() - ) - ; - std::vector> raw_notes = {raw_note}; - std::array last_note_end = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - better::Notes result; - for (const auto& [position, delay, duration]: raw_notes) { - if (duration > 0) { - const auto tail_6_notation = *gen::inRange(0, 6); - const auto tail_tip = better::convert_6_notation_to_position(position, tail_6_notation); - auto& end = last_note_end[position.index()]; - const auto time = end + delay; - end += delay + duration; - result.insert(better::LongNote{time, position, duration, tail_tip}); - } else { - auto& end = last_note_end[position.index()]; - const auto time = end + delay; - end += delay; - result.insert(better::TapNote{time, position}); + )); + std::array last_note_end = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + better::Notes result; + for (const auto& [position, delay, duration]: raw_notes) { + if (duration > 0) { + const auto tail_6_notation = *gen::inRange(0, 6); + const auto tail_tip = better::convert_6_notation_to_position(position, tail_6_notation); + auto& end = last_note_end[position.index()]; + const auto time = end + delay; + end += delay + duration; + result.insert(better::LongNote{time, position, duration, tail_tip}); + } else { + auto& end = last_note_end[position.index()]; + const auto time = end + delay; + end += delay; + result.insert(better::TapNote{time, position}); + } } - } - return gen::just(result); + return result; + }); + }; + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::apply([](unsigned int a) + gen::construct(gen::arbitrary()), + gen::construct(gen::arbitrary()) + ); + } + }; + + template<> + struct Arbitrary { + static Gen arbitrary() { + return gen::oneOf( + gen::construct(gen::arbitrary()), + gen::construct(gen::arbitrary()) + ); } }; } \ No newline at end of file diff --git a/src/tests/rapidcheck/main.cpp b/src/tests/rapidcheck/main.cpp index cf47fc5..315d3ea 100644 --- a/src/tests/rapidcheck/main.cpp +++ b/src/tests/rapidcheck/main.cpp @@ -1,26 +1,44 @@ #include +#include +#include +#include #include "generators.hpp" #include "../../better_note.hpp" +#include "../../better_notes.hpp" +#include "../../better_timing.hpp" int main() { + + rc::check( "A Note survives being converted to json and back", [](const better::Note& n) { const auto j = n.dump_to_memon_1_0_0(); + RC_LOG("json dump : "+j.dump()); const auto n_recovered = better::Note::load_from_memon_1_0_0(j); RC_ASSERT(n_recovered == n); } ); rc::check( - "A set of Notes survive being converted to json and back", - [](const better::Notes& ns) { - const auto j = ns.dump_to_memon_1_0_0(); - RC_TAG(j.dump()); - const auto n_recovered = better::Notes::load_from_memon_1_0_0(j); - RC_ASSERT(n_recovered == ns); + "A set of Notes survives being converted to json and back", + [](const better::Notes& original) { + const auto j = original.dump_to_memon_1_0_0(); + RC_LOG("json dump : "+j.dump()); + const auto recovered = better::Notes::load_from_memon_1_0_0(j); + RC_ASSERT(original == recovered); + } + ); + + rc::check( + "A Timing object survives being converted to json and back", + [](const better::Timing& original) { + const auto j = original.dump_to_memon_1_0_0(); + RC_LOG("json dump : "+j.dump()); + const auto recovered = better::Timing::load_from_memon_1_0_0(j); + RC_ASSERT(original == recovered); } ); return 0; diff --git a/src/tests/rapidcheck/meson.build b/src/tests/rapidcheck/meson.build index f651a60..1da7e4e 100644 --- a/src/tests/rapidcheck/meson.build +++ b/src/tests/rapidcheck/meson.build @@ -1,5 +1,6 @@ rapidcheck_tests = executable( 'rapidcheck_tests', + 'generators.cpp', 'main.cpp', '../../better_beats.cpp', '../../better_note.cpp', diff --git a/src/widgets/linear_view.cpp b/src/widgets/linear_view.cpp index 153e119..a75524b 100644 --- a/src/widgets/linear_view.cpp +++ b/src/widgets/linear_view.cpp @@ -102,23 +102,23 @@ void LinearView::update( while (next_beat_line_y < y) { if (next_beat % 4 == 0) { beat_line.setFillColor(sf::Color::White); - beat_line.setPosition({timeline_x, static_cast(next_beat_line_y.get_d())}); + beat_line.setPosition({timeline_x, static_cast(static_cast(next_beat_line_y))}); view.draw(beat_line); ss.str(std::string()); const Fraction measure = next_beat / 4; - ss << static_cast(measure.get_d()); + ss << static_cast(static_cast(measure)); beat_number.setString(ss.str()); sf::FloatRect textRect = beat_number.getLocalBounds(); beat_number.setOrigin( textRect.left + textRect.width, textRect.top + textRect.height / 2.f); - beat_number.setPosition({40.f, static_cast(next_beat_line_y.get_d())}); + beat_number.setPosition({40.f, static_cast(static_cast(next_beat_line_y))}); view.draw(beat_number); } else { beat_line.setFillColor(sf::Color(255, 255, 255, 127)); - beat_line.setPosition({timeline_x, static_cast(next_beat_line_y.get_d())}); + beat_line.setPosition({timeline_x, static_cast(static_cast(next_beat_line_y))}); view.draw(beat_line); } next_beat += 1; @@ -140,15 +140,15 @@ void LinearView::update( auto draw_note = VariantVisitor { [&, this](const better::TapNote& tap_note){ float note_x = timeline_x + note_width * (tap_note.get_position().index() + 0.5f); - float note_y = static_cast(beats_to_pixels_absolute.transform(tap_note.get_time()).get_d()); + float note_y = static_cast(beats_to_pixels_absolute.transform(tap_note.get_time())); const auto note_seconds = timing.time_at(tap_note.get_time()); const auto first_colliding_beat = timing.beats_at(note_seconds - sf::milliseconds(500)); const auto collision_zone_y = beats_to_pixels_absolute.transform(first_colliding_beat); const auto last_colliding_beat = timing.beats_at(note_seconds + sf::milliseconds(500)); const auto collision_zone_height = beats_to_pixels_proportional.transform(last_colliding_beat - first_colliding_beat); - note_collision_zone.setSize({collizion_zone_width, static_cast(collision_zone_height.get_d())}); + note_collision_zone.setSize({collizion_zone_width, static_cast(static_cast(collision_zone_height))}); Toolbox::set_local_origin_normalized(note_collision_zone, 0.5f, 0.f); - note_collision_zone.setPosition(note_x, static_cast(collision_zone_y.get_d())); + note_collision_zone.setPosition(note_x, static_cast(static_cast(collision_zone_y))); this->view.draw(note_collision_zone); tap_note_rect.setPosition(note_x, note_y); this->view.draw(tap_note_rect); @@ -159,19 +159,19 @@ void LinearView::update( }, [&, this](const better::LongNote& long_note){ float note_x = timeline_x + note_width * (long_note.get_position().index() + 0.5f); - float note_y = static_cast(beats_to_pixels_absolute.transform(long_note.get_time()).get_d()); + float note_y = static_cast(beats_to_pixels_absolute.transform(long_note.get_time())); const auto note_start_seconds = timing.time_at(long_note.get_time()); const auto first_colliding_beat = timing.beats_at(note_start_seconds - sf::milliseconds(500)); const auto collision_zone_y = beats_to_pixels_absolute.transform(first_colliding_beat); const auto note_end_seconds = timing.time_at(long_note.get_end()); const auto last_colliding_beat = timing.beats_at(note_end_seconds + sf::milliseconds(500)); const auto collision_zone_height = beats_to_pixels_proportional.transform(last_colliding_beat - first_colliding_beat); - note_collision_zone.setSize({collizion_zone_width, static_cast(collision_zone_height.get_d())}); + note_collision_zone.setSize({collizion_zone_width, static_cast(static_cast(collision_zone_height))}); Toolbox::set_local_origin_normalized(note_collision_zone, 0.5f, 0.f); - note_collision_zone.setPosition(note_x, static_cast(collision_zone_y.get_d())); + note_collision_zone.setPosition(note_x, static_cast(static_cast(collision_zone_y))); this->view.draw(note_collision_zone); const auto long_note_rect_height = beats_to_pixels_proportional.transform(long_note.get_duration()); - long_note_rect.setSize({long_note_rect_width, static_cast(long_note_rect_height.get_d())}); + long_note_rect.setSize({long_note_rect_width, static_cast(static_cast(long_note_rect_height))}); Toolbox::set_local_origin_normalized(long_note_rect, 0.5f, 0.f); long_note_rect.setPosition(note_x, note_y); this->view.draw(long_note_rect); @@ -212,9 +212,9 @@ void LinearView::update( if (pixel_interval.intersects({0, y})) { selection.setSize({ selection_width, - static_cast(pixel_interval.width().get_d()), + static_cast(static_cast(pixel_interval.width())), }); - selection.setPosition(timeline_x, static_cast(pixel_interval.start.get_d())); + selection.setPosition(timeline_x, static_cast(pixel_interval.start)); view.draw(selection); } }