From ec3779e1b4963a617857e3eac15f1fd62e5203fd Mon Sep 17 00:00:00 2001 From: Stepland <10530295-Buggyroom@users.noreply.gitlab.com> Date: Tue, 19 Apr 2022 12:59:17 +0200 Subject: [PATCH] Fix some bugs with the fractions --- src/editor_state.cpp | 1 + src/special_numeric_types.cpp | 30 +++++++++++++++++++++++++++--- src/tests/doctest/fractions.cpp | 19 ++++++++++++++++++- src/widgets/linear_view.cpp | 16 +++++++++------- src/widgets/linear_view.hpp | 1 + 5 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/editor_state.cpp b/src/editor_state.cpp index 7cd3f66..3f1b256 100644 --- a/src/editor_state.cpp +++ b/src/editor_state.cpp @@ -620,6 +620,7 @@ void EditorState::display_linear_view() { *chart_state, applicable_timing, current_exact_beats(), + beats_at(editable_range.end), get_snap_step(), ImGui::GetContentRegionMax() ); diff --git a/src/special_numeric_types.cpp b/src/special_numeric_types.cpp index ac47b83..c3eec67 100644 --- a/src/special_numeric_types.cpp +++ b/src/special_numeric_types.cpp @@ -2,6 +2,7 @@ #include #include +#include #include Fraction::Fraction(const Decimal& d) { @@ -90,7 +91,11 @@ Fraction operator%(Fraction lhs, const Fraction& rhs) { const auto db = rhs.value.get_den(); const auto na = lhs.value.get_num(); const auto nb = rhs.value.get_num(); - return Fraction{(na * db) % (nb * da), da * db}; + const mpz_class dividend = na * db; + const mpz_class divisor = nb * da; + mpz_class remainder; + mpz_mod(remainder.get_mpz_t(), dividend.get_mpz_t(), divisor.get_mpz_t()); + return Fraction{remainder, da * db}; }; std::strong_ordering operator<=>(const Fraction& lhs, const Fraction& rhs) { @@ -160,11 +165,30 @@ std::uint64_t convert_to_u64(const mpz_class& z) { } Fraction floor_fraction(const Fraction& f) { - return f - (f % Fraction{1}); + mpz_class result; + mpz_fdiv_q(result.get_mpz_t(), f.numerator().get_mpz_t(), f.denominator().get_mpz_t()); + return Fraction{result}; }; +// Thanks python again ! +// https://github.com/python/cpython/blob/f163ad22d3321cb9bb4e6cbaac5a723444641565/Lib/fractions.py#L612 Fraction round_fraction(const Fraction& f) { - return floor_fraction(f + Fraction{1, 2}); + mpz_class floor, remainder; + mpz_fdiv_qr( + floor.get_mpz_t(), + remainder.get_mpz_t(), + f.numerator().get_mpz_t(), + f.denominator().get_mpz_t() + ); + if (remainder * 2 < f.denominator()) { + return Fraction{floor}; + } else if (remainder * 2 > f.denominator()) { + return Fraction{floor + 1}; + } else if (floor % 2 == 0) { // Deal with the half case + return Fraction{floor}; + } else { + return floor + 1; + } }; Fraction convert_to_fraction(const Decimal& d) { diff --git a/src/tests/doctest/fractions.cpp b/src/tests/doctest/fractions.cpp index a10796e..0b65292 100644 --- a/src/tests/doctest/fractions.cpp +++ b/src/tests/doctest/fractions.cpp @@ -20,7 +20,7 @@ TEST_CASE("Fractions") { 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{-5,2}) == INT64_C(-3)); CHECK(static_cast(Fraction{"-9223372036854775808/1"}) == INT64_MIN); CHECK(static_cast(Fraction{"9223372036854775807/1"}) == INT64_MAX); CHECK(static_cast(Fraction{"-9000000000000000000/1"}) == INT64_C(-9000000000000000000)); @@ -71,6 +71,23 @@ TEST_CASE("Fractions") { CHECK(Fraction{1,2} / Fraction{1,7} == Fraction{7,2}); CHECK(Fraction{-1,2} / Fraction{1,2} == Fraction{-1,1}); } + SUBCASE("are modulo'ed correctly") { + CHECK(Fraction{3,2} % Fraction{1} == Fraction{1,2}); + CHECK(Fraction{23,14} % Fraction{2,5} == Fraction{3,70}); + CHECK(Fraction{-7,4} % Fraction{1} == Fraction{1,4}); + } + SUBCASE("are floor'ed correctly") { + CHECK(floor_fraction(Fraction{1,2}) == Fraction{0}); + CHECK(floor_fraction(Fraction{23,14}) == Fraction{1}); + CHECK(floor_fraction(Fraction{-7,4}) == Fraction{-2}); + } + SUBCASE("are rounded correctly") { + CHECK(round_fraction(Fraction{10,20}) == Fraction{0}); + CHECK(round_fraction(Fraction{11,20}) == Fraction{1}); + CHECK(round_fraction(Fraction{-10,20}) == Fraction{0}); + CHECK(round_fraction(Fraction{-11,20}) == Fraction{-1}); + } + SUBCASE("support binary operand with") { SUBCASE("integer literals") { CHECK(Fraction{1,2} + 1 == Fraction{3,2}); diff --git a/src/widgets/linear_view.cpp b/src/widgets/linear_view.cpp index b8b421b..e99b43a 100644 --- a/src/widgets/linear_view.cpp +++ b/src/widgets/linear_view.cpp @@ -58,6 +58,7 @@ void LinearView::update( const ChartState& chart_state, const better::Timing& timing, const Fraction& current_beat, + const Fraction& last_editable_beat, const Fraction& snap, const ImVec2& size ) { @@ -75,9 +76,9 @@ void LinearView::update( // cursor_y pixels and we use this fact to compute the rest const auto beats_before_cursor = beats_to_pixels_proportional.backwards_transform(cursor_y); const auto beats_after_cursor = beats_to_pixels_proportional.backwards_transform(static_cast(y) - cursor_y); - const Fraction first_visible_beat = current_beat - beats_before_cursor; - const Fraction last_visible_beat = current_beat + beats_after_cursor; - AffineTransform beats_to_pixels_absolute{first_visible_beat, last_visible_beat, 0, y}; + const Fraction first_beat_in_frame = current_beat - beats_before_cursor; + const Fraction last_beat_in_frame = current_beat + beats_after_cursor; + AffineTransform beats_to_pixels_absolute{first_beat_in_frame, last_beat_in_frame, 0, y}; float timeline_width = static_cast(x) - 80.f; float timeline_x = 50.f; @@ -89,7 +90,7 @@ void LinearView::update( beat_number.setCharacterSize(15); beat_number.setFillColor(sf::Color::White); - // Draw the beat lines and numbers + const Fraction first_visible_beat = std::max(Fraction{0}, first_beat_in_frame); auto next_beat = [](const auto& first_beat) -> Fraction { if (first_beat % 1 == 0) { return first_beat; @@ -98,9 +99,10 @@ void LinearView::update( } }(first_visible_beat); + // Draw the beat lines and numbers for ( Fraction next_beat_line_y = beats_to_pixels_absolute.transform(next_beat); - next_beat_line_y < y; + next_beat_line_y < y and next_beat < last_editable_beat; next_beat_line_y = beats_to_pixels_absolute.transform(next_beat += 1) ) { if (next_beat % 4 == 0) { @@ -182,9 +184,9 @@ void LinearView::update( }, }; - const auto first_visible_second = timing.time_at(first_visible_beat); + const auto first_visible_second = timing.time_at(first_beat_in_frame); const auto first_visible_collision_zone = timing.beats_at(first_visible_second - sf::milliseconds(500)); - const auto last_visible_second = timing.time_at(last_visible_beat); + const auto last_visible_second = timing.time_at(last_beat_in_frame); const auto last_visible_collision_zone = timing.beats_at(last_visible_second + sf::milliseconds(500)); chart_state.chart.notes.in( first_visible_collision_zone, diff --git a/src/widgets/linear_view.hpp b/src/widgets/linear_view.hpp index fa38ea6..304c8a4 100644 --- a/src/widgets/linear_view.hpp +++ b/src/widgets/linear_view.hpp @@ -18,6 +18,7 @@ public: const ChartState& chart_state, const better::Timing& timing, const Fraction& current_beat, + const Fraction& last_editable_beat, const Fraction& snap, const ImVec2& size );