+247
-28
@@ -86,10 +86,15 @@ 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 stem_head_vertical_overlap_spaces = std::float32_t{0.38F};
|
||||
constexpr auto measure_right_guard_spaces = std::float32_t{2.05F};
|
||||
constexpr auto chord_above_staff_spaces = std::float32_t{3.65F};
|
||||
constexpr auto chord_clearance_spaces = std::float32_t{0.45F};
|
||||
constexpr auto lyric_below_staff_spaces = std::float32_t{6.15F};
|
||||
constexpr auto lyric_clearance_spaces = std::float32_t{0.75F};
|
||||
constexpr auto tie_endpoint_gap_spaces = std::float32_t{0.28F};
|
||||
constexpr auto tie_note_offset_spaces = std::float32_t{0.50F};
|
||||
constexpr auto tie_arch_spaces = std::float32_t{0.72F};
|
||||
|
||||
auto to_raylib(luma::color color) -> Color {
|
||||
return {
|
||||
@@ -832,6 +837,26 @@ struct beam_group_layout {
|
||||
std::float32_t beam_y{};
|
||||
};
|
||||
|
||||
struct laid_out_tie_head {
|
||||
vamp_zero::score_pitch pitch{};
|
||||
luma::rect bounds{};
|
||||
std::float32_t center_y{};
|
||||
};
|
||||
|
||||
struct laid_out_note_ref {
|
||||
std::size_t system_index{};
|
||||
std::size_t measure_index{};
|
||||
std::size_t note_index{};
|
||||
luma::rect notation_bounds{};
|
||||
std::bool_t stem_up{true};
|
||||
std::vector<laid_out_tie_head> heads{};
|
||||
};
|
||||
|
||||
struct laid_out_measure_ref {
|
||||
std::size_t measure_index{};
|
||||
luma::rect notation_bounds{};
|
||||
};
|
||||
|
||||
auto layout_staff(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
std::float32_t x,
|
||||
@@ -1679,8 +1704,6 @@ auto layout_note_symbols(
|
||||
note_layout const& note,
|
||||
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 {
|
||||
@@ -1714,25 +1737,6 @@ auto layout_note_symbols(
|
||||
note.note_index);
|
||||
}
|
||||
}
|
||||
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 = 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,
|
||||
measure_index,
|
||||
note.note_index);
|
||||
}
|
||||
}
|
||||
|
||||
auto note_x(std::float32_t start_x, std::float32_t width, std::int64_t total_ticks, std::int64_t elapsed_ticks, std::int64_t duration_ticks) -> std::float32_t {
|
||||
@@ -1837,6 +1841,23 @@ auto measure_notation_top_y(
|
||||
return top_y;
|
||||
}
|
||||
|
||||
auto measure_notation_bottom_y(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 bottom_y = staff_top + spacing * 4.0F;
|
||||
for (auto const& note : notes) {
|
||||
if (!note.hidden) {
|
||||
bottom_y = std::max(bottom_y, note.visual_bottom);
|
||||
}
|
||||
}
|
||||
for (auto const& group : beam_groups) {
|
||||
bottom_y = std::max(bottom_y, group.beam_y + scaled(beam_thickness_spaces, spacing) * 0.5F);
|
||||
}
|
||||
return bottom_y;
|
||||
}
|
||||
|
||||
auto lyric_y(std::float32_t staff_top, std::float32_t spacing, std::float32_t notation_bottom_y) -> std::float32_t {
|
||||
return std::max(staff_top + scaled(lyric_below_staff_spaces, spacing), notation_bottom_y + scaled(lyric_clearance_spaces, spacing));
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1846,6 +1867,189 @@ auto chord_symbol_y(std::float32_t staff_top, std::float32_t spacing, std::float
|
||||
return y;
|
||||
}
|
||||
|
||||
auto same_pitch(vamp_zero::score_pitch const& left, vamp_zero::score_pitch const& right) -> std::bool_t {
|
||||
return normalized_letter(left.letter) == normalized_letter(right.letter)
|
||||
&& left.octave == right.octave
|
||||
&& pitch_alteration(left.accidental) == pitch_alteration(right.accidental);
|
||||
}
|
||||
|
||||
auto make_laid_out_note_ref(note_layout const& note, luma::rect notation_bounds, std::size_t system_index, std::size_t measure_index) -> laid_out_note_ref {
|
||||
auto ref = laid_out_note_ref{
|
||||
.system_index = system_index,
|
||||
.measure_index = measure_index,
|
||||
.note_index = note.note_index,
|
||||
.notation_bounds = notation_bounds,
|
||||
.stem_up = note.stem_up,
|
||||
};
|
||||
if (note.source == nullptr || note.hidden || note.rest) {
|
||||
return ref;
|
||||
}
|
||||
ref.heads.reserve(note.heads.size());
|
||||
for (auto const& head : note.heads) {
|
||||
if (head.source_pitch_index >= note.source->pitches.size()) {
|
||||
continue;
|
||||
}
|
||||
ref.heads.push_back({
|
||||
.pitch = note.source->pitches[head.source_pitch_index],
|
||||
.bounds = head.bounds,
|
||||
.center_y = head.center_y,
|
||||
});
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
|
||||
auto find_note_ref(std::vector<laid_out_note_ref> const& refs, std::size_t measure_index, std::size_t note_index) -> laid_out_note_ref const* {
|
||||
auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) {
|
||||
return ref.measure_index == measure_index && ref.note_index == note_index;
|
||||
});
|
||||
return found == refs.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
auto find_measure_ref(std::vector<laid_out_measure_ref> const& refs, std::size_t measure_index) -> laid_out_measure_ref const* {
|
||||
auto found = std::find_if(refs.begin(), refs.end(), [&](auto const& ref) {
|
||||
return ref.measure_index == measure_index;
|
||||
});
|
||||
return found == refs.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
auto find_tie_head(laid_out_note_ref const& ref, vamp_zero::score_pitch const& pitch) -> laid_out_tie_head const* {
|
||||
auto found = std::find_if(ref.heads.begin(), ref.heads.end(), [&](auto const& head) {
|
||||
return same_pitch(head.pitch, pitch);
|
||||
});
|
||||
return found == ref.heads.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
struct tied_note_destination {
|
||||
std::size_t measure_index{};
|
||||
std::size_t note_index{};
|
||||
};
|
||||
|
||||
auto find_tied_destination(vamp_zero::score_document const& score, std::size_t measure_index, std::size_t note_index, vamp_zero::score_pitch const& pitch) -> std::optional<tied_note_destination> {
|
||||
for (auto current_measure = measure_index; current_measure < score.measures.size(); ++current_measure) {
|
||||
auto const& measure = score.measures[current_measure];
|
||||
auto first_note = current_measure == measure_index ? note_index + 1U : 0U;
|
||||
for (auto current_note = first_note; current_note < measure.notes.size(); ++current_note) {
|
||||
auto const& note = measure.notes[current_note];
|
||||
if (note.hidden || note.kind == "rest") {
|
||||
continue;
|
||||
}
|
||||
auto matching_pitch = std::find_if(note.pitches.begin(), note.pitches.end(), [&](auto const& candidate) {
|
||||
return same_pitch(candidate, pitch);
|
||||
});
|
||||
if (matching_pitch == note.pitches.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (note.tie_from_previous) {
|
||||
return tied_note_destination{.measure_index = current_measure, .note_index = current_note};
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto add_tie_curve(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
luma::point from,
|
||||
luma::point to,
|
||||
std::bool_t below_note,
|
||||
std::float32_t spacing,
|
||||
std::size_t system_index,
|
||||
std::size_t measure_index,
|
||||
std::size_t note_index
|
||||
) -> void {
|
||||
if (to.x <= from.x + spacing * 0.40F) {
|
||||
return;
|
||||
}
|
||||
auto direction = below_note ? std::float32_t{1.0F} : std::float32_t{-1.0F};
|
||||
auto control = luma::point{
|
||||
.x = (from.x + to.x) * 0.5F,
|
||||
.y = (from.y + to.y) * 0.5F + direction * scaled(tie_arch_spaces, spacing),
|
||||
};
|
||||
add_curve(
|
||||
layout,
|
||||
vamp_zero::score_layout_item_role::TIE,
|
||||
from,
|
||||
control,
|
||||
to,
|
||||
at_least_one_pixel(scaled(tie_thickness_spaces, spacing)),
|
||||
score_ink(),
|
||||
system_index,
|
||||
measure_index,
|
||||
note_index);
|
||||
}
|
||||
|
||||
auto layout_page_ties(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
vamp_zero::score_document const& score,
|
||||
std::vector<laid_out_note_ref> const& note_refs,
|
||||
std::vector<laid_out_measure_ref> const& measure_refs,
|
||||
std::float32_t spacing
|
||||
) -> void {
|
||||
for (auto measure_index = std::size_t{}; measure_index < score.measures.size(); ++measure_index) {
|
||||
auto const& measure = score.measures[measure_index];
|
||||
for (auto note_index = std::size_t{}; note_index < measure.notes.size(); ++note_index) {
|
||||
auto const& note = measure.notes[note_index];
|
||||
if (!note.tie_to_next || note.hidden || note.kind == "rest") {
|
||||
continue;
|
||||
}
|
||||
auto const* start_ref = find_note_ref(note_refs, measure_index, note_index);
|
||||
for (auto const& pitch : note.pitches) {
|
||||
auto destination = find_tied_destination(score, measure_index, note_index, pitch);
|
||||
auto const* destination_ref = destination ? find_note_ref(note_refs, destination->measure_index, destination->note_index) : nullptr;
|
||||
auto const* start_head = start_ref == nullptr ? nullptr : find_tie_head(*start_ref, pitch);
|
||||
auto const* destination_head = destination_ref == nullptr ? nullptr : find_tie_head(*destination_ref, pitch);
|
||||
|
||||
if (start_ref != nullptr && start_head != nullptr && destination_ref != nullptr && destination_head != nullptr && start_ref->system_index == destination_ref->system_index) {
|
||||
auto below = start_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset},
|
||||
{.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
start_ref->system_index,
|
||||
start_ref->measure_index,
|
||||
start_ref->note_index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (start_ref != nullptr && start_head != nullptr) {
|
||||
auto below = start_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = start_head->bounds.x + start_head->bounds.width + scaled(tie_endpoint_gap_spaces, spacing), .y = start_head->center_y + y_offset},
|
||||
{.x = start_ref->notation_bounds.x + start_ref->notation_bounds.width, .y = start_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
start_ref->system_index,
|
||||
start_ref->measure_index,
|
||||
start_ref->note_index);
|
||||
}
|
||||
if (destination && destination_ref != nullptr && destination_head != nullptr) {
|
||||
auto const* measure_ref = find_measure_ref(measure_refs, destination->measure_index);
|
||||
if (measure_ref == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto below = destination_ref->stem_up;
|
||||
auto y_offset = scaled(tie_note_offset_spaces, spacing) * (below ? 1.0F : -1.0F);
|
||||
add_tie_curve(
|
||||
layout,
|
||||
{.x = measure_ref->notation_bounds.x, .y = destination_head->center_y + y_offset},
|
||||
{.x = destination_head->bounds.x - scaled(tie_endpoint_gap_spaces, spacing), .y = destination_head->center_y + y_offset},
|
||||
below,
|
||||
spacing,
|
||||
destination_ref->system_index,
|
||||
destination_ref->measure_index,
|
||||
destination_ref->note_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto layout_measure(
|
||||
vamp_zero::score_page_layout& layout,
|
||||
vamp_zero::score_document const& score,
|
||||
@@ -1855,7 +2059,9 @@ auto layout_measure(
|
||||
std::size_t system_index,
|
||||
std::size_t measure_index,
|
||||
std::float32_t staff_top,
|
||||
std::float32_t spacing
|
||||
std::float32_t spacing,
|
||||
std::vector<laid_out_note_ref>& note_refs,
|
||||
std::vector<laid_out_measure_ref>& measure_refs
|
||||
) -> 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);
|
||||
|
||||
@@ -1863,6 +2069,10 @@ auto layout_measure(
|
||||
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};
|
||||
measure_refs.push_back({
|
||||
.measure_index = measure_index,
|
||||
.notation_bounds = notation_bounds,
|
||||
});
|
||||
auto total_ticks = measure_ticks(measure);
|
||||
auto elapsed = std::int64_t{};
|
||||
auto accidentals = make_accidental_state(effective_key_signature(score, measure));
|
||||
@@ -1889,7 +2099,12 @@ auto layout_measure(
|
||||
}
|
||||
|
||||
auto const notation_top_y = measure_notation_top_y(measure, notes, beam_groups, staff_top, spacing);
|
||||
auto const notation_bottom_y = measure_notation_bottom_y(notes, beam_groups, staff_top, spacing);
|
||||
auto const chord_y = chord_symbol_y(staff_top, spacing, notation_top_y);
|
||||
auto const lyrics_y = lyric_y(staff_top, spacing, notation_bottom_y);
|
||||
for (auto const& note : notes) {
|
||||
note_refs.push_back(make_laid_out_note_ref(note, notation_bounds, system_index, measure_index));
|
||||
}
|
||||
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);
|
||||
@@ -1948,14 +2163,13 @@ auto layout_measure(
|
||||
add_flag(layout, note, spacing, system_index, measure_index);
|
||||
}
|
||||
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, notation_bounds.x + notation_bounds.width, system_index, measure_index);
|
||||
layout_note_symbols(layout, notes[index], staff_top, spacing, system_index, measure_index);
|
||||
if (notes[index].source != nullptr && !notes[index].hidden && !notes[index].source->lyric.empty()) {
|
||||
add_text(
|
||||
layout,
|
||||
vamp_zero::score_layout_item_role::LYRIC,
|
||||
notes[index].x - spacing,
|
||||
staff_top + spacing * 5.35F,
|
||||
lyrics_y,
|
||||
notes[index].source->lyric,
|
||||
1,
|
||||
score_muted_ink(),
|
||||
@@ -2027,6 +2241,8 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
auto const content_width = std::max<std::float32_t>(spacing * 18.0F, bounds.width - margin * 2.0F);
|
||||
auto const system_height = (bounds.height - 108.0F) / static_cast<std::float32_t>(vamp_zero::score_systems_per_page);
|
||||
auto const first_measure = page_index * vamp_zero::score_measures_per_page;
|
||||
auto note_refs = std::vector<laid_out_note_ref>{};
|
||||
auto measure_refs = std::vector<laid_out_measure_ref>{};
|
||||
|
||||
for (auto system = std::size_t{}; system < vamp_zero::score_systems_per_page; ++system) {
|
||||
auto const system_y = content_y + static_cast<std::float32_t>(system) * system_height;
|
||||
@@ -2052,7 +2268,9 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
system,
|
||||
measure_index,
|
||||
staff_top,
|
||||
spacing);
|
||||
spacing,
|
||||
note_refs,
|
||||
measure_refs);
|
||||
}
|
||||
|
||||
auto const bar_x = x + measure_width;
|
||||
@@ -2069,6 +2287,7 @@ auto vamp_zero::layout_score_page(luma::rect bounds, vamp_zero::score_document c
|
||||
}
|
||||
}
|
||||
|
||||
layout_page_ties(layout, score, note_refs, measure_refs, spacing);
|
||||
add_stroke_rect(layout, vamp_zero::score_layout_item_role::BORDER, bounds, 1.1F, score_border());
|
||||
return layout;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user