Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lyric improvements #3905

Merged
merged 9 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/vrv/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ class Options {
OptionDbl m_ledgerLineThickness;
OptionDbl m_ledgerLineExtension;
OptionIntMap m_lyricElision;
OptionDbl m_lyricHeightFactor;
OptionDbl m_lyricLineThickness;
OptionBool m_lyricNoStartHyphen;
OptionDbl m_lyricSize;
Expand Down
4 changes: 3 additions & 1 deletion include/vrv/syl.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ class Syl : public LayerElement,
* The verse number with multiple verses
* Value is 1 by default, set in PrepareLyrics
*/
int m_drawingVerse;
int m_drawingVerseN;
/** The verse place (below by default) */
data_STAFFREL m_drawingVersePlace;

/**
* A pointer to the next syllable of the word.
Expand Down
7 changes: 6 additions & 1 deletion include/vrv/verse.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ class Syl;
// Verse
//----------------------------------------------------------------------------

class Verse : public LayerElement, public AttColor, public AttLang, public AttNInteger, public AttTypography {
class Verse : public LayerElement,
public AttColor,
public AttLang,
public AttNInteger,
public AttPlacementRelStaff,
public AttTypography {
public:
/**
* @name Constructors, destructors, and other standard methods
Expand Down
12 changes: 8 additions & 4 deletions include/vrv/verticalaligner.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,12 @@ class StaffAlignment : public Object {
* The position is calculated from the bottom.
*/
///@{
void AddVerseN(int verseN);
void AddVerseN(int verseN, data_STAFFREL place);
int GetVerseCount(bool collapse) const;
int GetVersePosition(int verseN, bool collapse) const;
int GetVerseCountAbove(bool collapse) const;
int GetVerseCountBelow(bool collapse) const;
int GetVersePositionAbove(int verseN, bool collapse) const;
int GetVersePositionBelow(int verseN, bool collapse) const;
///@}

/**
Expand Down Expand Up @@ -402,9 +405,10 @@ class StaffAlignment : public Object {
*/
int m_yRel;
/**
* Stores the verse@n of the staves attached to the aligner
* Stores the verse@n of the staves attached to the aligner (above and below
lpugin marked this conversation as resolved.
Show resolved Hide resolved
*/
std::set<int> m_verseNs;
std::set<int> m_verseAboveNs;
std::set<int> m_verseBelowNs;

/**
* @name values for storing the overflow and overlap
Expand Down
2 changes: 1 addition & 1 deletion include/vrv/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ class View {
std::u32string IntToTimeSigFigures(unsigned short number);
std::u32string IntToSmuflFigures(unsigned short number, int offset);
int NestedTuplets(Object *object);
int GetSylYRel(int verseN, Staff *staff);
int GetSylYRel(int verseN, Staff *staff, data_STAFFREL place);
int GetFYRel(F *f, Staff *staff);
///@}

Expand Down
35 changes: 24 additions & 11 deletions src/adjustfloatingpositionerfunctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,33 @@ FunctorCode AdjustFloatingPositionersFunctor::VisitStaffAlignment(StaffAlignment

staffAlignment->SortPositioners();

const bool verseCollapse = m_doc->GetOptions()->m_lyricVerseCollapse.GetValue();
if (m_classId == SYL) {
const bool verseCollapse = m_doc->GetOptions()->m_lyricVerseCollapse.GetValue();
if (staffAlignment->GetVerseCount(verseCollapse) > 0) {
FontInfo *lyricFont = m_doc->GetDrawingLyricFont(staffAlignment->GetStaff()->m_drawingStaffSize);
int descender = m_doc->GetTextGlyphDescender(L'q', lyricFont, false);
int height = m_doc->GetTextGlyphHeight(L'I', lyricFont, false);
int margin = m_doc->GetBottomMargin(SYL) * drawingUnit;
int minMargin = std::max((int)(m_doc->GetOptions()->m_lyricTopMinMargin.GetValue() * drawingUnit),
staffAlignment->GetOverflowBelow());
staffAlignment->SetOverflowBelow(
minMargin + staffAlignment->GetVerseCount(verseCollapse) * (height - descender + margin));
// For now just clear the overflowBelow, which avoids the overlap to be calculated. We could also keep them
// and check if they are some lyrics in order to know if the overlap needs to be calculated or not.
staffAlignment->ClearBBoxesBelow();
int verseHeight = m_doc->GetTextGlyphHeight(L'I', lyricFont, false)
- m_doc->GetTextGlyphDescender(L'q', lyricFont, false);
verseHeight *= m_doc->GetOptions()->m_lyricHeightFactor.GetValue();
if (staffAlignment->GetVerseCountAbove(verseCollapse)) {
int margin = m_doc->GetTopMargin(SYL) * drawingUnit;
int minMargin = std::max((int)(m_doc->GetOptions()->m_lyricTopMinMargin.GetValue() * drawingUnit),
staffAlignment->GetOverflowAbove());
staffAlignment->SetOverflowAbove(
minMargin + staffAlignment->GetVerseCountAbove(verseCollapse) * (verseHeight + margin));
// For now just clear the overflowBelow, which avoids the overlap to be calculated. We could also keep
// them and check if they are some lyrics in order to know if the overlap needs to be calculated or not.
staffAlignment->ClearBBoxesAbove();
}
if (staffAlignment->GetVerseCountBelow(verseCollapse)) {
int margin = m_doc->GetBottomMargin(SYL) * drawingUnit;
int minMargin = std::max((int)(m_doc->GetOptions()->m_lyricTopMinMargin.GetValue() * drawingUnit),
staffAlignment->GetOverflowBelow());
staffAlignment->SetOverflowBelow(
minMargin + staffAlignment->GetVerseCountBelow(verseCollapse) * (verseHeight + margin));
// For now just clear the overflowBelow, which avoids the overlap to be calculated. We could also keep
// them and check if they are some lyrics in order to know if the overlap needs to be calculated or not.
lpugin marked this conversation as resolved.
Show resolved Hide resolved
staffAlignment->ClearBBoxesBelow();
}
}
return FUNCTOR_SIBLINGS;
}
Expand Down
18 changes: 11 additions & 7 deletions src/alignfunctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,9 +640,9 @@ FunctorCode AlignVerticallyFunctor::VisitStaff(Staff *staff)
std::vector<Object *>::const_iterator verseIterator = std::find_if(
staff->m_timeSpanningElements.begin(), staff->m_timeSpanningElements.end(), ObjectComparison(VERSE));
if (verseIterator != staff->m_timeSpanningElements.end()) {
Verse *v = vrv_cast<Verse *>(*verseIterator);
assert(v);
alignment->AddVerseN(v->GetN());
Verse *verse = vrv_cast<Verse *>(*verseIterator);
assert(verse);
alignment->AddVerseN(verse->GetN(), verse->GetPlace());
}

// add verse number to alignment in case there are spanning SYL elements but there is no verse number already - this
Expand All @@ -653,9 +653,13 @@ FunctorCode AlignVerticallyFunctor::VisitStaff(Staff *staff)
Verse *verse = vrv_cast<Verse *>((*sylIterator)->GetFirstAncestor(VERSE));
if (verse) {
const int verseNumber = verse->GetN();
const data_STAFFREL versePlace = verse->GetPlace();
const bool verseCollapse = m_doc->GetOptions()->m_lyricVerseCollapse.GetValue();
if (!alignment->GetVersePosition(verseNumber, verseCollapse)) {
alignment->AddVerseN(verseNumber);
if ((versePlace == STAFFREL_above) && !alignment->GetVersePositionAbove(verseNumber, verseCollapse)) {
alignment->AddVerseN(verseNumber, verse->GetPlace());
}
if ((versePlace != STAFFREL_above) && !alignment->GetVersePositionBelow(verseNumber, verseCollapse)) {
lpugin marked this conversation as resolved.
Show resolved Hide resolved
alignment->AddVerseN(verseNumber, verse->GetPlace());
}
}
}
Expand Down Expand Up @@ -685,7 +689,7 @@ FunctorCode AlignVerticallyFunctor::VisitSyllable(Syllable *syllable)
StaffAlignment *alignment = m_systemAligner->GetStaffAlignmentForStaffN(m_staffN);
if (!alignment) return FUNCTOR_CONTINUE;
// Current limitation of only one syl (verse n) by syllable
alignment->AddVerseN(1);
alignment->AddVerseN(1, STAFFREL_below);

return FUNCTOR_CONTINUE;
}
Expand Down Expand Up @@ -720,7 +724,7 @@ FunctorCode AlignVerticallyFunctor::VisitVerse(Verse *verse)
if (!alignment) return FUNCTOR_CONTINUE;

// Add the number count
alignment->AddVerseN(verse->GetN());
alignment->AddVerseN(verse->GetN(), verse->GetPlace());

return FUNCTOR_CONTINUE;
}
Expand Down
2 changes: 2 additions & 0 deletions src/iomei.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,7 @@ void MEIOutput::WriteVerse(pugi::xml_node currentNode, Verse *verse)
verse->WriteColor(currentNode);
verse->WriteLang(currentNode);
verse->WriteNInteger(currentNode);
verse->WritePlacementRelStaff(currentNode);
verse->WriteTypography(currentNode);
}

Expand Down Expand Up @@ -7163,6 +7164,7 @@ bool MEIInput::ReadVerse(Object *parent, pugi::xml_node verse)
vrvVerse->ReadColor(verse);
vrvVerse->ReadLang(verse);
vrvVerse->ReadNInteger(verse);
vrvVerse->ReadPlacementRelStaff(verse);
vrvVerse->ReadTypography(verse);

parent->AddChild(vrvVerse);
Expand Down
4 changes: 4 additions & 0 deletions src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,10 @@ Options::Options()
m_lyricElision.Init(ELISION_regular, &Option::s_elision);
this->Register(&m_lyricElision, "lyricElision", &m_generalLayout);

m_lyricHeightFactor.SetInfo("Lyric height factor", "The lyric verse line height factor");
m_lyricHeightFactor.Init(1.0, 1.0, 20.0);
this->Register(&m_lyricHeightFactor, "lyricHeightFactor", &m_generalLayout);

m_lyricLineThickness.SetInfo("Lyric line thickness", "The lyric extender line thickness");
m_lyricLineThickness.Init(0.25, 0.10, 0.50);
this->Register(&m_lyricLineThickness, "lyricLineThickness", &m_generalLayout);
Expand Down
3 changes: 2 additions & 1 deletion src/preparedatafunctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,8 @@ FunctorCode PrepareLyricsFunctor::VisitSyl(Syl *syl)
{
Verse *verse = vrv_cast<Verse *>(syl->GetFirstAncestor(VERSE, MAX_NOTE_DEPTH));
if (verse) {
syl->m_drawingVerse = std::max(verse->GetN(), 1);
syl->m_drawingVerseN = std::max(verse->GetN(), 1);
syl->m_drawingVersePlace = verse->GetPlace();
}

syl->SetStart(vrv_cast<LayerElement *>(syl->GetFirstAncestor(NOTE, MAX_NOTE_DEPTH)));
Expand Down
3 changes: 2 additions & 1 deletion src/syl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ void Syl::Reset()
this->ResetTypography();
this->ResetSylLog();

m_drawingVerse = 1;
m_drawingVerseN = 1;
m_drawingVersePlace = STAFFREL_below;
m_nextWordSyl = NULL;
}

Expand Down
2 changes: 2 additions & 0 deletions src/verse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ Verse::Verse() : LayerElement(VERSE, "verse-"), AttColor(), AttLang(), AttNInteg
this->RegisterAttClass(ATT_COLOR);
this->RegisterAttClass(ATT_LANG);
this->RegisterAttClass(ATT_NINTEGER);
this->RegisterAttClass(ATT_PLACEMENTRELSTAFF);
this->RegisterAttClass(ATT_TYPOGRAPHY);

this->Reset();
Expand All @@ -51,6 +52,7 @@ void Verse::Reset()
this->ResetColor();
this->ResetLang();
this->ResetNInteger();
this->ResetPlacementRelStaff();
this->ResetTypography();

m_drawingLabelAbbr = NULL;
Expand Down
57 changes: 46 additions & 11 deletions src/verticalaligner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,8 @@ FunctorCode SystemAligner::AcceptEnd(ConstFunctor &functor) const
StaffAlignment::StaffAlignment() : Object(STAFF_ALIGNMENT)
{
m_yRel = 0;
m_verseNs.clear();
m_verseAboveNs.clear();
m_verseBelowNs.clear();
m_staff = NULL;
m_floatingPositionersSorted = true;

Expand Down Expand Up @@ -436,39 +437,73 @@ void StaffAlignment::SetRequestedSpaceBelow(int space)
}
}

void StaffAlignment::AddVerseN(int verseN)
void StaffAlignment::AddVerseN(int verseN, data_STAFFREL place)
{
// if 0, then assume 1;
verseN = std::max(verseN, 1);
m_verseNs.insert(verseN);
(place == STAFFREL_above) ? m_verseAboveNs.insert(verseN) : m_verseBelowNs.insert(verseN);
}

int StaffAlignment::GetVerseCount(bool collapse) const
{
if (m_verseNs.empty()) {
return (this->GetVerseCountAbove(collapse) + this->GetVerseCountBelow(collapse));
}

int StaffAlignment::GetVerseCountAbove(bool collapse) const
{
if (m_verseAboveNs.empty()) {
return 0;
}
else if (collapse) {
return (int)m_verseAboveNs.size();
}
else {
return (*m_verseAboveNs.rbegin());
}
}

int StaffAlignment::GetVerseCountBelow(bool collapse) const
{
if (m_verseBelowNs.empty()) {
return 0;
}
else if (collapse) {
return (int)m_verseNs.size();
return (int)m_verseBelowNs.size();
}
else {
return (*m_verseBelowNs.rbegin());
}
}

int StaffAlignment::GetVersePositionAbove(int verseN, bool collapse) const
{
if (m_verseAboveNs.empty()) {
// Syl in neumatic notation - since verse count will be 0, position is -1
return -1;
}
else if (collapse) {
auto it = std::find(m_verseAboveNs.begin(), m_verseAboveNs.end(), verseN);
int pos = (int)std::distance(m_verseAboveNs.begin(), it);
return pos;
}
else {
return *m_verseNs.rbegin();
return verseN - 1;
}
}

int StaffAlignment::GetVersePosition(int verseN, bool collapse) const
int StaffAlignment::GetVersePositionBelow(int verseN, bool collapse) const
{
if (m_verseNs.empty()) {
if (m_verseBelowNs.empty()) {
// Syl in neumatic notation - since verse count will be 0, position is -1
return -1;
}
else if (collapse) {
auto it = std::find(m_verseNs.rbegin(), m_verseNs.rend(), verseN);
int pos = (int)std::distance(m_verseNs.rbegin(), it);
auto it = std::find(m_verseBelowNs.rbegin(), m_verseBelowNs.rend(), verseN);
int pos = (int)std::distance(m_verseBelowNs.rbegin(), it);
return pos;
}
else {
return (*m_verseNs.rbegin()) - verseN;
return (*m_verseBelowNs.rbegin()) - verseN;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/view_control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ void View::DrawSylConnector(
assert(syl->GetStart() && syl->GetEnd());
if (!syl->GetStart() || !syl->GetEnd()) return;

const int y = staff->GetDrawingY() + this->GetSylYRel(syl->m_drawingVerse, staff);
const int y = staff->GetDrawingY() + this->GetSylYRel(syl->m_drawingVerseN, staff, syl->m_drawingVersePlace);

// Invalid bounding boxes might occur for empty syllables without text child
if (!syl->HasContentHorizontalBB()) return;
Expand Down
32 changes: 22 additions & 10 deletions src/view_element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1753,7 +1753,7 @@ void View::DrawSyl(DeviceContext *dc, LayerElement *element, Layer *layer, Staff
}

if (!m_doc->IsFacs() && !m_doc->IsTranscription() && !m_doc->IsNeumeLines()) {
syl->SetDrawingYRel(this->GetSylYRel(syl->m_drawingVerse, staff));
syl->SetDrawingYRel(this->GetSylYRel(syl->m_drawingVerseN, staff, syl->m_drawingVersePlace));
}

dc->StartGraphic(syl, "", syl->GetID());
Expand Down Expand Up @@ -1871,7 +1871,7 @@ void View::DrawVerse(DeviceContext *dc, LayerElement *element, Layer *layer, Sta

TextDrawingParams params;
params.m_x = verse->GetDrawingX() - m_doc->GetDrawingUnit(staff->m_drawingStaffSize);
params.m_y = staff->GetDrawingY() + this->GetSylYRel(std::max(1, verse->GetN()), staff);
params.m_y = staff->GetDrawingY() + this->GetSylYRel(std::max(1, verse->GetN()), staff, verse->GetPlace());
params.m_pointSize = labelTxt.GetPointSize();

dc->SetBrush(m_currentColor, AxSOLID);
Expand Down Expand Up @@ -2098,22 +2098,34 @@ int View::GetFYRel(F *f, Staff *staff)
return y;
}

int View::GetSylYRel(int verseN, Staff *staff)
int View::GetSylYRel(int verseN, Staff *staff, data_STAFFREL place)
{
assert(staff);

StaffAlignment *alignment = staff->GetAlignment();
if (!alignment) return 0;

const bool verseCollapse = m_options->m_lyricVerseCollapse.GetValue();
int y = 0;
StaffAlignment *alignment = staff->GetAlignment();
if (alignment) {
FontInfo *lyricFont = m_doc->GetDrawingLyricFont(staff->m_drawingStaffSize);
int descender = -m_doc->GetTextGlyphDescender(L'q', lyricFont, false);
int height = m_doc->GetTextGlyphHeight(L'I', lyricFont, false);
int margin = m_doc->GetBottomMargin(SYL) * m_doc->GetDrawingUnit(staff->m_drawingStaffSize);

FontInfo *lyricFont = m_doc->GetDrawingLyricFont(staff->m_drawingStaffSize);
const int descender = m_doc->GetTextGlyphDescender(L'q', lyricFont, false);
const int height = m_doc->GetTextGlyphHeight(L'I', lyricFont, false);

int verseHeight = height - descender;
verseHeight *= m_doc->GetOptions()->m_lyricHeightFactor.GetValue();
int margin = m_doc->GetBottomMargin(SYL) * m_doc->GetDrawingUnit(staff->m_drawingStaffSize);

// above the staff
if (place == STAFFREL_above) {
y = alignment->GetOverflowAbove()
- (alignment->GetVersePositionAbove(verseN, verseCollapse)) * (verseHeight + margin) - (height);
}
else {
y = -alignment->GetStaffHeight() - alignment->GetOverflowBelow()
+ alignment->GetVersePosition(verseN, verseCollapse) * (height + descender + margin) + (descender);
+ alignment->GetVersePositionBelow(verseN, verseCollapse) * (verseHeight + margin) + verseHeight - height;
}

return y;
}

Expand Down
Loading