Fix some bugs with the fractions

This commit is contained in:
Stepland 2022-04-19 12:59:17 +02:00
parent 7e93ea6845
commit ec3779e1b4
5 changed files with 56 additions and 11 deletions

View File

@ -620,6 +620,7 @@ void EditorState::display_linear_view() {
*chart_state, *chart_state,
applicable_timing, applicable_timing,
current_exact_beats(), current_exact_beats(),
beats_at(editable_range.end),
get_snap_step(), get_snap_step(),
ImGui::GetContentRegionMax() ImGui::GetContentRegionMax()
); );

View File

@ -2,6 +2,7 @@
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
#include <gmpxx.h>
#include <stdexcept> #include <stdexcept>
Fraction::Fraction(const Decimal& d) { 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 db = rhs.value.get_den();
const auto na = lhs.value.get_num(); const auto na = lhs.value.get_num();
const auto nb = rhs.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) { 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) { 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) { 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) { Fraction convert_to_fraction(const Decimal& d) {

View File

@ -20,7 +20,7 @@ TEST_CASE("Fractions") {
SUBCASE("std::int64_t, returing the integral part") { SUBCASE("std::int64_t, returing the integral part") {
CHECK(static_cast<std::int64_t>(Fraction{1,2}) == INT64_C(0)); CHECK(static_cast<std::int64_t>(Fraction{1,2}) == INT64_C(0));
CHECK(static_cast<std::int64_t>(Fraction{2,3}) == INT64_C(0)); CHECK(static_cast<std::int64_t>(Fraction{2,3}) == INT64_C(0));
CHECK(static_cast<std::int64_t>(Fraction{-5,2}) == INT64_C(-2)); CHECK(static_cast<std::int64_t>(Fraction{-5,2}) == INT64_C(-3));
CHECK(static_cast<std::int64_t>(Fraction{"-9223372036854775808/1"}) == INT64_MIN); CHECK(static_cast<std::int64_t>(Fraction{"-9223372036854775808/1"}) == INT64_MIN);
CHECK(static_cast<std::int64_t>(Fraction{"9223372036854775807/1"}) == INT64_MAX); CHECK(static_cast<std::int64_t>(Fraction{"9223372036854775807/1"}) == INT64_MAX);
CHECK(static_cast<std::int64_t>(Fraction{"-9000000000000000000/1"}) == INT64_C(-9000000000000000000)); CHECK(static_cast<std::int64_t>(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,7} == Fraction{7,2});
CHECK(Fraction{-1,2} / Fraction{1,2} == Fraction{-1,1}); 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("support binary operand with") {
SUBCASE("integer literals") { SUBCASE("integer literals") {
CHECK(Fraction{1,2} + 1 == Fraction{3,2}); CHECK(Fraction{1,2} + 1 == Fraction{3,2});

View File

@ -58,6 +58,7 @@ void LinearView::update(
const ChartState& chart_state, const ChartState& chart_state,
const better::Timing& timing, const better::Timing& timing,
const Fraction& current_beat, const Fraction& current_beat,
const Fraction& last_editable_beat,
const Fraction& snap, const Fraction& snap,
const ImVec2& size const ImVec2& size
) { ) {
@ -75,9 +76,9 @@ void LinearView::update(
// cursor_y pixels and we use this fact to compute the rest // 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_before_cursor = beats_to_pixels_proportional.backwards_transform(cursor_y);
const auto beats_after_cursor = beats_to_pixels_proportional.backwards_transform(static_cast<float>(y) - cursor_y); const auto beats_after_cursor = beats_to_pixels_proportional.backwards_transform(static_cast<float>(y) - cursor_y);
const Fraction first_visible_beat = current_beat - beats_before_cursor; const Fraction first_beat_in_frame = current_beat - beats_before_cursor;
const Fraction last_visible_beat = current_beat + beats_after_cursor; const Fraction last_beat_in_frame = current_beat + beats_after_cursor;
AffineTransform<Fraction> beats_to_pixels_absolute{first_visible_beat, last_visible_beat, 0, y}; AffineTransform<Fraction> beats_to_pixels_absolute{first_beat_in_frame, last_beat_in_frame, 0, y};
float timeline_width = static_cast<float>(x) - 80.f; float timeline_width = static_cast<float>(x) - 80.f;
float timeline_x = 50.f; float timeline_x = 50.f;
@ -89,7 +90,7 @@ void LinearView::update(
beat_number.setCharacterSize(15); beat_number.setCharacterSize(15);
beat_number.setFillColor(sf::Color::White); 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 { auto next_beat = [](const auto& first_beat) -> Fraction {
if (first_beat % 1 == 0) { if (first_beat % 1 == 0) {
return first_beat; return first_beat;
@ -98,9 +99,10 @@ void LinearView::update(
} }
}(first_visible_beat); }(first_visible_beat);
// Draw the beat lines and numbers
for ( for (
Fraction next_beat_line_y = beats_to_pixels_absolute.transform(next_beat); 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) next_beat_line_y = beats_to_pixels_absolute.transform(next_beat += 1)
) { ) {
if (next_beat % 4 == 0) { 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 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)); const auto last_visible_collision_zone = timing.beats_at(last_visible_second + sf::milliseconds(500));
chart_state.chart.notes.in( chart_state.chart.notes.in(
first_visible_collision_zone, first_visible_collision_zone,

View File

@ -18,6 +18,7 @@ public:
const ChartState& chart_state, const ChartState& chart_state,
const better::Timing& timing, const better::Timing& timing,
const Fraction& current_beat, const Fraction& current_beat,
const Fraction& last_editable_beat,
const Fraction& snap, const Fraction& snap,
const ImVec2& size const ImVec2& size
); );