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
+10
View File
@@ -37,6 +37,15 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
pico_sdk_init()
set(VAMP_MODULUS_INCLUDE_DIR "" CACHE PATH "Path to the Modulus public include directory")
if(NOT VAMP_MODULUS_INCLUDE_DIR)
set(VAMP_MODULUS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../modulus/include")
endif()
get_filename_component(VAMP_MODULUS_INCLUDE_DIR "${VAMP_MODULUS_INCLUDE_DIR}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
if(NOT EXISTS "${VAMP_MODULUS_INCLUDE_DIR}/modulus/types.hpp")
message(FATAL_ERROR "VAMP_MODULUS_INCLUDE_DIR must contain modulus/types.hpp: ${VAMP_MODULUS_INCLUDE_DIR}")
endif()
add_executable(product-vamp-pico2w
src/vamp_style.cpp
src/vamp_style/font.cpp
@@ -48,6 +57,7 @@ add_executable(product-vamp-pico2w
)
target_include_directories(product-vamp-pico2w PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${VAMP_MODULUS_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include/vamp_pico/capi
)
target_compile_definitions(product-vamp-pico2w PRIVATE
+7
View File
@@ -33,6 +33,9 @@ target_include_directories(vamp-style PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_compile_features(vamp-style PUBLIC cxx_std_17)
target_link_libraries(vamp-style PUBLIC
modulus::modulus
)
set(VAMP_PROTOCOL_SOURCES
src/vamp_protocol/page_control.cpp
@@ -43,6 +46,9 @@ target_include_directories(vamp-protocol PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_compile_features(vamp-protocol PUBLIC cxx_std_17)
target_link_libraries(vamp-protocol PUBLIC
modulus::modulus
)
set(VAMP_CORE_SOURCES
src/vamp_zero/app.cpp
@@ -205,6 +211,7 @@ if(VAMP_BUILD_PICO_FIRMWARE)
"${VAMP_PICO_WIFI_PASSPHRASE}"
"${VAMP_ZERO_HOST}"
"${VAMP_ZERO_PORT}"
"${modulus_INCLUDE_DIR}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
package/scripts/build_pico_firmware.sh
-25
View File
@@ -1,25 +0,0 @@
#pragma once
#include <cstdint>
#if defined(__has_include)
#if __has_include(<stdfloat>)
#include <stdfloat>
#endif
#endif
namespace std {
using bool_t = decltype(true);
using char_t = decltype('x');
using uchar_t = std::uint8_t;
#if !defined(__STDCPP_FLOAT32_T__)
using float32_t = decltype(0.0F);
#endif
#if !defined(__STDCPP_FLOAT64_T__)
using float64_t = decltype(0.0);
#endif
}
+1 -1
View File
@@ -3,7 +3,7 @@
#include <array>
#include <cstdint>
#include <types.hpp>
#include <modulus/types.hpp>
#include <vamp_style.hpp>
namespace vamp_pico {
+1 -1
View File
@@ -1,7 +1,7 @@
#pragma once
#include <vamp_protocol/page_control.hpp>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_pico {
+1 -1
View File
@@ -2,7 +2,7 @@
#include <cstdint>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_pico {
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_protocol {
+1 -1
View File
@@ -2,7 +2,7 @@
#include <cstdint>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_style {
+1 -1
View File
@@ -4,7 +4,7 @@
#include <cstdint>
#include <string_view>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_style {
+1 -1
View File
@@ -6,7 +6,7 @@
#include <modulus.hpp>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_zero {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <luma.hpp>
#include <modulus.hpp>
#include <types.hpp>
#include <modulus/types.hpp>
#include <vamp_zero/types.hpp>
namespace vamp_zero {
+1 -1
View File
@@ -6,7 +6,7 @@
#include <string_view>
#include <vector>
#include <types.hpp>
#include <modulus/types.hpp>
namespace vamp_zero {
+11 -2
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ne 7 ]; then
echo "usage: build_pico_firmware.sh BUILD_DIR OUTPUT_UF2 SDK_VERSION WIFI_SSID WIFI_PASSPHRASE ZERO_HOST ZERO_PORT" >&2
if [ "$#" -lt 7 ] || [ "$#" -gt 8 ]; then
echo "usage: build_pico_firmware.sh BUILD_DIR OUTPUT_UF2 SDK_VERSION WIFI_SSID WIFI_PASSPHRASE ZERO_HOST ZERO_PORT [MODULUS_INCLUDE_DIR]" >&2
exit 2
fi
@@ -14,6 +14,7 @@ wifi_ssid=$4
wifi_passphrase=$5
zero_host=$6
zero_port=$7
modulus_include_dir=${8:-}
pico_sdk_path=${VAMP_PICO_SDK_PATH:-${PICO_SDK_PATH:-}}
sdk_args=(
@@ -27,6 +28,13 @@ if [ -n "$pico_sdk_path" ]; then
-DPICO_SDK_FETCH_FROM_GIT=OFF
)
fi
modulus_args=()
if [ -n "$modulus_include_dir" ]; then
modulus_include_dir=$(cd "$modulus_include_dir" && pwd)
modulus_args=(
"-DVAMP_MODULUS_INCLUDE_DIR=${modulus_include_dir}"
)
fi
mkdir -p "$(dirname "$output_uf2")"
@@ -58,6 +66,7 @@ cmake \
-DPICO_BOARD=pico2_w \
-DPICO_GCC_TRIPLE=arm-none-eabi \
"${sdk_args[@]}" \
"${modulus_args[@]}" \
"-DVAMP_PICO_WIFI_SSID=${wifi_ssid}" \
"-DVAMP_PICO_WIFI_PASSPHRASE=${wifi_passphrase}" \
"-DVAMP_ZERO_HOST=${zero_host}" \
+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,
+76 -3
View File
@@ -154,6 +154,10 @@ auto center_y(vamp_zero::score_layout_item const& item) -> std::float32_t {
return item.bounds.y + item.bounds.height * 0.5F;
}
auto right_edge(vamp_zero::score_layout_item const& item) -> std::float32_t {
return item.bounds.x + item.bounds.width;
}
auto near(std::float32_t left, std::float32_t right) -> std::bool_t {
return std::abs(left - right) < 0.02F;
}
@@ -325,7 +329,11 @@ BOOST_AUTO_TEST_CASE(score_layout_orders_preamble_on_staff_baselines) {
BOOST_TEST(clefs.front()->bounds.x < keys.front()->bounds.x);
BOOST_TEST(keys.front()->bounds.x < times.front()->bounds.x);
BOOST_TEST(near(clefs.front()->origin.y, staff_lines.front()->from.y + layout.staff_spacing * 3.0F));
BOOST_TEST(near(clefs.front()->origin.y, staff_lines.front()->from.y + layout.staff_spacing * 3.5F));
BOOST_TEST(near(keys.front()->origin.y, staff_lines.front()->from.y + layout.staff_spacing * 2.5F));
BOOST_REQUIRE_GE(times.size(), 2U);
BOOST_TEST(near(times[0]->origin.y, staff_lines.front()->from.y + layout.staff_spacing * 1.5F));
BOOST_TEST(near(times[1]->origin.y, staff_lines.front()->from.y + layout.staff_spacing * 3.5F));
}
BOOST_AUTO_TEST_CASE(score_layout_attaches_stems_to_notehead_anchors) {
@@ -337,8 +345,10 @@ BOOST_AUTO_TEST_CASE(score_layout_attaches_stems_to_notehead_anchors) {
BOOST_REQUIRE_EQUAL(heads.size(), 1U);
BOOST_REQUIRE_EQUAL(stems.size(), 1U);
BOOST_TEST(near(stems.front()->from.x, heads.front()->origin.x + layout.staff_spacing * 1.336F));
BOOST_TEST(near(stems.front()->from.y, heads.front()->origin.y - layout.staff_spacing * 0.288F));
BOOST_TEST(stems.front()->from.x < heads.front()->bounds.x + heads.front()->bounds.width);
BOOST_TEST(stems.front()->from.x > heads.front()->bounds.x + heads.front()->bounds.width - layout.staff_spacing * 0.25F);
BOOST_TEST(stems.front()->from.y > heads.front()->bounds.y);
BOOST_TEST(stems.front()->from.y < heads.front()->bounds.y + heads.front()->bounds.height);
BOOST_TEST(stems.front()->to.y < stems.front()->from.y);
}
@@ -403,3 +413,66 @@ BOOST_AUTO_TEST_CASE(score_layout_beams_eighth_tuplets_and_labels_them) {
BOOST_REQUIRE_EQUAL(tuplets.size(), 1U);
BOOST_TEST(tuplets.front()->text == "3");
}
BOOST_AUTO_TEST_CASE(score_layout_keeps_dense_notation_inside_measure_guard) {
auto first = note_with_ticks({pitch('c', "#")}, 2048);
auto second = note_with_ticks({pitch('d', "#")}, 2048);
auto third = note_with_ticks({pitch('e', "n")}, 6144);
auto fourth = note_with_ticks({pitch('f', "#")}, 4096);
auto fifth = note_with_ticks({pitch('g', "#")}, 2048);
fourth.tie_to_next = true;
auto score = score_with_measure({
.time_signature = "4/4",
.key_signature = "C",
.notes = {std::move(first), std::move(second), std::move(third), std::move(fourth), std::move(fifth)},
});
auto layout = vamp_zero::layout_score_page({.x = 0.0F, .y = 0.0F, .width = 976.0F, .height = 484.0F}, score, 0U);
auto barlines = layout_items(layout, vamp_zero::score_layout_item_role::FINAL_BARLINE, 0U, 0U);
BOOST_REQUIRE_EQUAL(barlines.size(), 1U);
auto guarded_right = barlines.front()->from.x - layout.staff_spacing;
auto roles = {
vamp_zero::score_layout_item_role::NOTEHEAD,
vamp_zero::score_layout_item_role::ACCIDENTAL,
vamp_zero::score_layout_item_role::STEM,
vamp_zero::score_layout_item_role::FLAG,
vamp_zero::score_layout_item_role::BEAM,
vamp_zero::score_layout_item_role::DOT,
vamp_zero::score_layout_item_role::TIE,
};
for (auto role : roles) {
for (auto const* item : layout_items(layout, role, 0U, 0U)) {
BOOST_TEST(right_edge(*item) <= guarded_right);
}
}
}
BOOST_AUTO_TEST_CASE(score_layout_raises_chord_symbols_above_high_notation) {
auto high = note({pitch('c', "n", 6)});
high.chord_symbol = "Cmaj7";
auto score = score_with_notes({std::move(high)});
auto layout = vamp_zero::layout_score_page({.x = 0.0F, .y = 0.0F, .width = 976.0F, .height = 484.0F}, score, 0U);
auto chords = layout_items(layout, vamp_zero::score_layout_item_role::CHORD_SYMBOL, 0U, 0U);
BOOST_REQUIRE_EQUAL(chords.size(), 1U);
auto top_y = std::numeric_limits<std::float32_t>::max();
auto roles = {
vamp_zero::score_layout_item_role::NOTEHEAD,
vamp_zero::score_layout_item_role::ACCIDENTAL,
vamp_zero::score_layout_item_role::STEM,
vamp_zero::score_layout_item_role::FLAG,
vamp_zero::score_layout_item_role::BEAM,
vamp_zero::score_layout_item_role::DOT,
vamp_zero::score_layout_item_role::TUPLET,
vamp_zero::score_layout_item_role::TUPLET_BRACKET,
vamp_zero::score_layout_item_role::TIE,
};
for (auto role : roles) {
for (auto const* item : layout_items(layout, role, 0U, 0U)) {
top_y = std::min(top_y, item->bounds.y);
}
}
BOOST_TEST(right_edge(*chords.front()) <= layout.bounds.x + layout.bounds.width);
BOOST_TEST(chords.front()->bounds.y + chords.front()->bounds.height <= top_y - layout.staff_spacing * 0.40F);
}