Type Cleanup & Rendering Fixes
ci / checks (push) Failing after 23m53s

This commit is contained in:
khodowany
2026-09-06 11:21:28 -05:00
parent ca285d63d4
commit 6f3f26a3df
15 changed files with 289 additions and 58 deletions
+176 -19
View File
@@ -84,6 +84,12 @@ constexpr auto thin_barline_thickness_spaces = std::float32_t{0.16F};
constexpr auto thick_barline_thickness_spaces = std::float32_t{0.50F};
constexpr auto tie_thickness_spaces = std::float32_t{0.13F};
constexpr auto normal_stem_length_spaces = std::float32_t{3.50F};
constexpr auto preamble_vertical_correction_spaces = std::float32_t{0.50F};
constexpr auto stem_head_horizontal_overlap_spaces = std::float32_t{0.06F};
constexpr auto stem_head_vertical_overlap_spaces = std::float32_t{0.20F};
constexpr auto measure_right_guard_spaces = std::float32_t{1.50F};
constexpr auto chord_above_staff_spaces = std::float32_t{3.65F};
constexpr auto chord_clearance_spaces = std::float32_t{0.45F};
auto to_raylib(luma::color color) -> Color {
return {
@@ -810,6 +816,8 @@ struct note_layout {
std::float32_t high_y{};
std::float32_t visual_left{};
std::float32_t visual_right{};
std::float32_t visual_top{};
std::float32_t visual_bottom{};
std::float32_t stem_x{};
std::float32_t stem_base_y{};
std::float32_t stem_tip_y{};
@@ -851,7 +859,7 @@ auto layout_clef(vamp_zero::score_page_layout& layout, std::string_view clef, st
auto reference_pitch = codepoint == glyph_f_clef
? vamp_zero::score_pitch{.letter = 'f', .octave = 3}
: vamp_zero::score_pitch{.letter = 'g', .octave = 4};
auto baseline_y = pitch_y(reference_pitch, staff_top, spacing, clef);
auto baseline_y = pitch_y(reference_pitch, staff_top, spacing, clef) + scaled(preamble_vertical_correction_spaces, spacing);
auto origin = origin_for_left_baseline(codepoint, x, baseline_y, spacing);
add_glyph(layout, vamp_zero::score_layout_item_role::CLEF, codepoint, origin, spacing, score_ink(), system_index);
return glyph_width(codepoint, spacing) + spacing * 0.90F;
@@ -924,7 +932,7 @@ auto layout_key_signature(
auto const total = static_cast<std::size_t>(std::abs(count));
for (auto index = std::size_t{}; index < total; ++index) {
auto pitch = key_signature_pitch(count, index, clef);
auto baseline_y = pitch_y(pitch, staff_top, spacing, clef);
auto baseline_y = pitch_y(pitch, staff_top, spacing, clef) + scaled(preamble_vertical_correction_spaces, spacing);
add_glyph(
layout,
vamp_zero::score_layout_item_role::KEY_SIGNATURE,
@@ -1006,8 +1014,8 @@ auto layout_time_signature(
if (width <= 0.0F) {
return 0.0F;
}
layout_time_signature_run(layout, numerator, x + (width - numerator_width) * 0.5F, staff_top + spacing, spacing, system_index);
layout_time_signature_run(layout, denominator, x + (width - denominator_width) * 0.5F, staff_top + spacing * 3.0F, spacing, system_index);
layout_time_signature_run(layout, numerator, x + (width - numerator_width) * 0.5F, staff_top + spacing * 1.5F, spacing, system_index);
layout_time_signature_run(layout, denominator, x + (width - denominator_width) * 0.5F, staff_top + spacing * 3.5F, spacing, system_index);
return width + spacing * 0.95F;
}
@@ -1140,13 +1148,18 @@ auto update_note_visual_extents(note_layout& note, std::float32_t spacing) -> vo
if (note.hidden) {
note.visual_left = note.x;
note.visual_right = note.x;
note.visual_top = note.low_y;
note.visual_bottom = note.low_y;
return;
}
if (note.rest) {
auto codepoint = rest_codepoint(note.duration);
auto half_width = glyph_width(codepoint, spacing) * 0.5F;
note.visual_left = note.x - half_width;
note.visual_right = note.x + half_width;
auto origin = origin_for_centered_baseline(codepoint, note.x, note.low_y, spacing);
auto bounds = glyph_bounds(codepoint, origin, spacing);
note.visual_left = bounds.x;
note.visual_right = bounds.x + bounds.width;
note.visual_top = bounds.y;
note.visual_bottom = bounds.y + bounds.height;
if (note.duration.dots != 0) {
note.visual_right += spacing * 1.35F;
}
@@ -1154,9 +1167,13 @@ auto update_note_visual_extents(note_layout& note, std::float32_t spacing) -> vo
}
note.visual_left = note.heads.empty() ? note.x : note.heads.front().bounds.x;
note.visual_right = note.heads.empty() ? note.x : note.heads.front().bounds.x + note.heads.front().bounds.width;
note.visual_top = note.heads.empty() ? note.high_y : note.heads.front().bounds.y;
note.visual_bottom = note.heads.empty() ? note.low_y : note.heads.front().bounds.y + note.heads.front().bounds.height;
for (auto const& head : note.heads) {
note.visual_left = std::min(note.visual_left, head.bounds.x);
note.visual_right = std::max(note.visual_right, head.bounds.x + head.bounds.width);
note.visual_top = std::min(note.visual_top, head.bounds.y);
note.visual_bottom = std::max(note.visual_bottom, head.bounds.y + head.bounds.height);
}
if (note.duration.dots != 0) {
note.visual_right += spacing * 1.40F;
@@ -1261,8 +1278,12 @@ auto update_stem_geometry(note_layout& note, std::float32_t staff_top, std::floa
auto anchor = note.stem_up ? stem_up_anchor(note.notehead) : stem_down_anchor(note.notehead);
auto base = anchor_point(head->origin, anchor, spacing);
auto center = staff_top + spacing * 2.0F;
note.stem_x = base.x;
note.stem_base_y = base.y;
note.stem_x = note.stem_up
? base.x - scaled(stem_head_horizontal_overlap_spaces, spacing)
: base.x + scaled(stem_head_horizontal_overlap_spaces, spacing);
note.stem_base_y = note.stem_up
? base.y + scaled(stem_head_vertical_overlap_spaces, spacing)
: base.y - scaled(stem_head_vertical_overlap_spaces, spacing);
note.stem_tip_y = note.stem_up
? std::min(note.high_y, center) - scaled(normal_stem_length_spaces, spacing)
: std::max(note.low_y, center) + scaled(normal_stem_length_spaces, spacing);
@@ -1416,6 +1437,24 @@ auto fit_note_spacing(std::vector<note_layout>& notes, luma::rect bounds, std::f
}
}
auto keep_notes_inside_right_guard(std::vector<note_layout>& notes, luma::rect bounds, std::float32_t spacing) -> void {
auto last_right = std::numeric_limits<std::float32_t>::lowest();
for (auto const& note : notes) {
if (!note.hidden) {
last_right = std::max(last_right, note.visual_right);
}
}
auto const right_limit = bounds.x + bounds.width - spacing * 0.25F;
if (last_right <= right_limit) {
return;
}
for (auto& note : notes) {
if (!note.hidden) {
shift_note_x(note, right_limit - last_right, spacing);
}
}
}
auto eligible_for_beam(note_layout const& note) -> std::bool_t {
return !note.hidden && !note.rest && has_stem(note.duration) && note.duration.base == duration_base::EIGHTH;
}
@@ -1560,6 +1599,55 @@ auto add_flag(vamp_zero::score_page_layout& layout, note_layout const& note, std
add_glyph(layout, vamp_zero::score_layout_item_role::FLAG, codepoint, origin, spacing, score_ink(), system_index, measure_index, note.note_index);
}
auto include_visual_bounds(note_layout& note, luma::rect bounds) -> void {
note.visual_left = std::min(note.visual_left, bounds.x);
note.visual_right = std::max(note.visual_right, bounds.x + bounds.width);
note.visual_top = std::min(note.visual_top, bounds.y);
note.visual_bottom = std::max(note.visual_bottom, bounds.y + bounds.height);
}
auto flag_bounds(note_layout const& note, std::float32_t spacing) -> luma::rect {
auto codepoint = note.stem_up ? glyph_flag_eighth_up : glyph_flag_eighth_down;
auto anchor = note.stem_up ? smufl_anchor{.x = 0.0F, .y = -0.176F} : smufl_anchor{.x = 0.0F, .y = 0.176F};
auto origin = luma::point{
.x = note.stem_x - anchor.x * spacing,
.y = note.stem_tip_y + anchor.y * spacing,
};
return glyph_bounds(codepoint, origin, spacing);
}
auto refresh_note_visual_extents(note_layout& note, std::float32_t spacing) -> void {
update_note_visual_extents(note, spacing);
if (note.hidden || note.rest) {
return;
}
auto accidental_extent = note_accidental_extent(note, spacing);
if (accidental_extent > 0.0F) {
note.visual_left -= accidental_extent;
}
for (auto const& head : note.heads) {
if (auto glyph = accidental_glyph(head.accidental)) {
auto box = glyph_box(*glyph);
note.visual_top = std::min(note.visual_top, head.center_y - box.north * spacing);
note.visual_bottom = std::max(note.visual_bottom, head.center_y - box.south * spacing);
}
}
if (has_stem(note.duration) && note.stem_x != 0.0F) {
auto half_stem = at_least_one_pixel(scaled(stem_thickness_spaces, spacing)) * 0.5F;
include_visual_bounds(note, {
.x = note.stem_x - half_stem,
.y = std::min(note.stem_base_y, note.stem_tip_y),
.width = half_stem * 2.0F,
.height = std::abs(note.stem_tip_y - note.stem_base_y),
});
}
if (has_flag(note.duration) && !note.beamed && note.stem_x != 0.0F) {
include_visual_bounds(note, flag_bounds(note, spacing));
}
}
auto layout_rest(
vamp_zero::score_page_layout& layout,
note_layout const& note,
@@ -1592,6 +1680,7 @@ auto layout_note_symbols(
std::float32_t staff_top,
std::float32_t spacing,
std::float32_t next_x,
std::float32_t measure_right_x,
std::size_t system_index,
std::size_t measure_index
) -> void {
@@ -1627,12 +1716,17 @@ auto layout_note_symbols(
}
if (note.source != nullptr && note.source->tie_to_next) {
auto tie_y = note.low_y + spacing * 0.62F;
auto tie_start_x = note.visual_right - spacing * 0.45F;
auto tie_end_x = std::min(next_x - spacing * 0.45F, measure_right_x - spacing * 0.30F);
if (tie_end_x <= tie_start_x + spacing * 0.50F) {
return;
}
add_curve(
layout,
vamp_zero::score_layout_item_role::TIE,
{.x = note.visual_right - spacing * 0.45F, .y = tie_y},
{.x = (note.x + next_x) * 0.5F, .y = tie_y + spacing * 0.78F},
{.x = next_x - spacing * 0.45F, .y = tie_y},
{.x = tie_start_x, .y = tie_y},
{.x = (tie_start_x + tie_end_x) * 0.5F, .y = tie_y + spacing * 0.78F},
{.x = tie_end_x, .y = tie_y},
at_least_one_pixel(scaled(tie_thickness_spaces, spacing)),
score_ink(),
system_index,
@@ -1703,6 +1797,55 @@ auto layout_tuplets(
}
}
auto measure_notation_top_y(
vamp_zero::score_measure const& measure,
std::vector<note_layout> const& notes,
std::vector<beam_group_layout> const& beam_groups,
std::float32_t staff_top,
std::float32_t spacing
) -> std::float32_t {
auto top_y = staff_top;
for (auto const& note : notes) {
if (!note.hidden) {
top_y = std::min(top_y, note.visual_top);
}
}
for (auto const& group : beam_groups) {
top_y = std::min(top_y, group.beam_y - scaled(beam_thickness_spaces, spacing) * 0.5F);
}
for (auto const& tuplet : measure.tuplets) {
if (tuplet.id.empty() || tuplet.note_count <= 0) {
continue;
}
auto first = invalid_layout_index;
auto last = invalid_layout_index;
for (auto index = std::size_t{}; index < notes.size(); ++index) {
if (notes[index].source != nullptr && notes[index].source->tuplet_id == tuplet.id) {
first = std::min(first, index);
last = index;
}
}
if (first == invalid_layout_index || last == invalid_layout_index || first > last) {
continue;
}
auto tuplet_top_y = staff_top;
for (auto index = first; index <= last; ++index) {
tuplet_top_y = std::min(tuplet_top_y, notes[index].beamed ? notes[index].stem_tip_y : notes[index].visual_top);
}
top_y = std::min(top_y, tuplet_top_y - spacing * 1.80F);
}
return top_y;
}
auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_top_y) -> std::float32_t {
auto y = staff_top - scaled(chord_above_staff_spaces, spacing);
auto const bottom_limit = notation_top_y - scaled(chord_clearance_spaces, spacing);
if (y + text_line_height(2) > bottom_limit) {
y = bottom_limit - text_line_height(2);
}
return y;
}
auto layout_measure(
vamp_zero::score_page_layout& layout,
vamp_zero::score_document const& score,
@@ -1715,12 +1858,11 @@ auto layout_measure(
std::float32_t spacing
) -> void {
add_text(layout, vamp_zero::score_layout_item_role::MEASURE_NUMBER, bounds.x + spacing * 0.20F, bounds.y + spacing * 0.05F, std::to_string(measure_number), 1, score_muted_ink(), spacing * 4.0F, system_index, measure_index);
if (!measure.section_label.empty()) {
add_text(layout, vamp_zero::score_layout_item_role::SECTION_LABEL, bounds.x + spacing * 2.10F, staff_top - spacing * 4.05F, measure.section_label, 2, score_accent_ink(), bounds.width - spacing * 2.6F, system_index, measure_index);
}
auto notation_x = bounds.x + spacing * 1.15F;
auto notation_width = std::max<std::float32_t>(spacing * 3.0F, bounds.width - (notation_x - bounds.x) - spacing * 0.80F);
auto notation_right_x = bounds.x + bounds.width - scaled(measure_right_guard_spaces, spacing);
auto notation_width = std::max<std::float32_t>(spacing * 3.0F, notation_right_x - notation_x);
auto notation_bounds = luma::rect{.x = notation_x, .y = bounds.y, .width = notation_width, .height = bounds.height};
auto total_ticks = measure_ticks(measure);
auto elapsed = std::int64_t{};
auto accidentals = make_accidental_state(effective_key_signature(score, measure));
@@ -1735,8 +1877,23 @@ auto layout_measure(
notes.push_back(build_note_layout(score, note, index, elapsed, duration_ticks, x, staff_top, spacing, accidental_marks));
elapsed += duration_ticks;
}
fit_note_spacing(notes, {.x = notation_x, .y = bounds.y, .width = notation_width, .height = bounds.height}, spacing);
fit_note_spacing(notes, notation_bounds, spacing);
auto beam_groups = make_beam_groups(notes, staff_top, spacing);
for (auto& note : notes) {
refresh_note_visual_extents(note, spacing);
}
fit_note_spacing(notes, notation_bounds, spacing);
keep_notes_inside_right_guard(notes, notation_bounds, spacing);
for (auto& note : notes) {
refresh_note_visual_extents(note, spacing);
}
auto const notation_top_y = measure_notation_top_y(measure, notes, beam_groups, staff_top, spacing);
auto const chord_y = chord_symbol_y(staff_top, spacing, notation_top_y);
if (!measure.section_label.empty()) {
auto section_y = std::min(staff_top - spacing * 4.70F, chord_y - spacing * 0.65F);
add_text(layout, vamp_zero::score_layout_item_role::SECTION_LABEL, bounds.x + spacing * 2.10F, section_y, measure.section_label, 2, score_accent_ink(), bounds.width - spacing * 2.6F, system_index, measure_index);
}
auto previous_chord = std::string{};
for (auto const& note : notes) {
@@ -1747,7 +1904,7 @@ auto layout_measure(
layout,
vamp_zero::score_layout_item_role::CHORD_SYMBOL,
note.x - spacing * 1.15F,
staff_top - spacing * 2.45F,
chord_y,
note.source->chord_symbol,
2,
score_ink(),
@@ -1792,7 +1949,7 @@ auto layout_measure(
}
for (auto index = std::size_t{}; index < notes.size(); ++index) {
auto next_x = index + 1U < notes.size() ? notes[index + 1U].x : notes[index].x + spacing * 3.0F;
layout_note_symbols(layout, notes[index], staff_top, spacing, next_x, system_index, measure_index);
layout_note_symbols(layout, notes[index], staff_top, spacing, next_x, notation_bounds.x + notation_bounds.width, system_index, measure_index);
if (notes[index].source != nullptr && !notes[index].hidden && !notes[index].source->lyric.empty()) {
add_text(
layout,