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,
applicable_timing,
current_exact_beats(),
beats_at(editable_range.end),
get_snap_step(),
ImGui::GetContentRegionMax()
);

View File

@ -2,6 +2,7 @@
#include <cmath>
#include <cstddef>
#include <gmpxx.h>
#include <stdexcept>
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) {

View File

@ -20,7 +20,7 @@ TEST_CASE("Fractions") {
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{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{"9223372036854775807/1"}) == INT64_MAX);
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,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});

View File

@ -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<float>(y) - cursor_y);
const Fraction first_visible_beat = current_beat - beats_before_cursor;
const Fraction last_visible_beat = current_beat + beats_after_cursor;
AffineTransform<Fraction> 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<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_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,

View File

@ -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
);