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

Small HarmonyLayout tidy #26647

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
256 changes: 117 additions & 139 deletions src/engraving/rendering/score/harmonylayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,153 +43,24 @@ void HarmonyLayout::layoutHarmonies(const std::vector<Segment*>& sl, LayoutConte
{
for (const Segment* s : sl) {
for (EngravingItem* e : s->annotations()) {
if (e->isHarmony()) {
Harmony* h = toHarmony(e);
// For chord symbols that coincide with a chord or rest,
// a partial layout can also happen (if needed) during ChordRest layout
// in order to calculate a bbox and allocate its shape to the ChordRest.
// But that layout (if it happens at all) does not do autoplace,
// so we need the full layout here.
TLayout::layoutHarmony(h, h->mutldata(), ctx);
Autoplace::autoplaceSegmentElement(h, h->mutldata());
if (!e->isHarmony()) {
continue;
}
Harmony* h = toHarmony(e);
// For chord symbols that coincide with a chord or rest,
// a partial layout can also happen (if needed) during ChordRest layout
// in order to calculate a bbox and allocate its shape to the ChordRest.
// But that layout (if it happens at all) does not do autoplace,
// so we need the full layout here.
TLayout::layoutHarmony(h, h->mutldata(), ctx);
Autoplace::autoplaceSegmentElement(h, h->mutldata());
}
}
}

void HarmonyLayout::alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool harmony, const double maxShiftAbove,
const double maxShiftBelow)
{
// Help class.
// Contains harmonies/fretboard per segment.
class HarmonyList : public std::vector<EngravingItem*>
{
OBJECT_ALLOCATOR(engraving, HarmonyList)

std::map<const Segment*, std::vector<EngravingItem*> > elements;
std::vector<EngravingItem*> modified;

EngravingItem* getReferenceElement(const Segment* s, bool above, bool visible) const
{
// Returns the reference element for aligning.
// When a segments contains multiple harmonies/fretboard, the lowest placed
// element (for placement above, otherwise the highest placed element) is
// used for alignment.
EngravingItem* element { nullptr };
for (EngravingItem* e : elements.at(s)) {
// Only chord symbols have styled offset, fretboards don't.
if (!e->autoplace() || (e->isHarmony() && !e->isStyled(Pid::OFFSET)) || (visible && !e->visible())) {
continue;
}
if (!element) {
element = e;
} else {
if ((e->placeAbove() && above && (element->y() < e->y()))
|| (e->placeBelow() && !above && (element->y() > e->y()))) {
element = e;
}
}
}
return element;
}

public:
HarmonyList()
{
elements.clear();
modified.clear();
}

void append(const Segment* s, EngravingItem* e)
{
elements[s].push_back(e);
}

double getReferenceHeight(bool above) const
{
// The reference height is the height of
// the lowest element if placed above
// or
// the highest element if placed below.
bool first { true };
double ref { 0.0 };
for (auto s : muse::keys(elements)) {
EngravingItem* e { getReferenceElement(s, above, true) };
if (!e) {
continue;
}
if (e->placeAbove() && above) {
ref = first ? e->y() : std::min(ref, e->y());
first = false;
} else if (e->placeBelow() && !above) {
ref = first ? e->y() : std::max(ref, e->y());
first = false;
}
}
return ref;
}

bool align(bool above, double reference, double maxShift)
{
// Align the elements. If a segment contains multiple elements,
// only the reference elements is used in the algorithm. All other
// elements will remain their original placement with respect to
// the reference element.
bool moved { false };
if (muse::RealIsNull(reference)) {
return moved;
}

for (auto s : muse::keys(elements)) {
std::list<EngravingItem*> handled;
EngravingItem* be = getReferenceElement(s, above, false);
if (!be) {
// If there are only invisible elements, we have to use an invisible
// element for alignment reference.
be = getReferenceElement(s, above, true);
}
if (be && ((above && (be->y() < (reference + maxShift))) || ((!above && (be->y() > (reference - maxShift)))))) {
double shift = be->ldata()->pos().y();
be->mutldata()->setPosY(reference - be->ryoffset());
shift -= be->ldata()->pos().y();
for (EngravingItem* e : elements[s]) {
if ((above && e->placeBelow()) || (!above && e->placeAbove())) {
continue;
}
modified.push_back(e);
handled.push_back(e);
moved = true;
if (e != be) {
e->mutldata()->moveY(-shift);
}
}
for (auto e : handled) {
muse::remove(elements[s], e);
}
}
}
return moved;
}

void addToSkyline(const System* system)
{
for (EngravingItem* e : modified) {
const Segment* s = toSegment(e->explicitParent());
const MeasureBase* m = toMeasureBase(s->explicitParent());
system->staff(e->staffIdx())->skyline().add(e->shape().translated(e->pos() + s->pos() + m->pos()));
if (e->isFretDiagram()) {
FretDiagram* fd = toFretDiagram(e);
Harmony* h = fd->harmony();
if (h) {
system->staff(e->staffIdx())->skyline().add(h->shape().translated(h->pos() + fd->pos() + s->pos() + m->pos()));
} else {
system->staff(e->staffIdx())->skyline().add(fd->shape().translated(fd->pos() + s->pos() + m->pos()));
}
}
}
}
};

if (muse::RealIsNull(maxShiftAbove) && muse::RealIsNull(maxShiftBelow)) {
return;
}
Expand Down Expand Up @@ -223,3 +94,110 @@ void HarmonyLayout::alignHarmonies(const System* system, const std::vector<Segme
staves[idx].addToSkyline(system);
}
}

EngravingItem* HarmonyList::getReferenceElement(const Segment* s, bool above, bool visible) const
{
// Returns the reference element for aligning.
// When a segments contains multiple harmonies/fretboard, the lowest placed
// element (for placement above, otherwise the highest placed element) is
// used for alignment.
EngravingItem* element { nullptr };
for (EngravingItem* e : elements.at(s)) {
// Only chord symbols have styled offset, fretboards don't.
if (!e->autoplace() || (e->isHarmony() && !e->isStyled(Pid::OFFSET)) || (visible && !e->visible())) {
continue;
}
if (!element) {
element = e;
} else if ((e->placeAbove() && above && (element->y() < e->y()))
|| (e->placeBelow() && !above && (element->y() > e->y()))) {
element = e;
}
}
return element;
}

double HarmonyList::getReferenceHeight(bool above) const
{
// The reference height is the height of
// the lowest element if placed above
// or
// the highest element if placed below.
bool first { true };
double ref { 0.0 };
for (auto s : muse::keys(elements)) {
EngravingItem* e { getReferenceElement(s, above, true) };
if (!e) {
continue;
}
if (e->placeAbove() && above) {
ref = first ? e->y() : std::min(ref, e->y());
first = false;
} else if (e->placeBelow() && !above) {
ref = first ? e->y() : std::max(ref, e->y());
first = false;
}
}
return ref;
}

bool HarmonyList::align(bool above, double reference, double maxShift)
{
// Align the elements. If a segment contains multiple elements,
// only the reference elements is used in the algorithm. All other
// elements will remain their original placement with respect to
// the reference element.
bool moved { false };
if (muse::RealIsNull(reference)) {
return moved;
}

for (auto s : muse::keys(elements)) {
std::list<EngravingItem*> handled;
EngravingItem* be = getReferenceElement(s, above, false);
if (!be) {
// If there are only invisible elements, we have to use an invisible
// element for alignment reference.
be = getReferenceElement(s, above, true);
}
if (be && ((above && (be->y() < (reference + maxShift))) || ((!above && (be->y() > (reference - maxShift)))))) {
double shift = be->ldata()->pos().y();
be->mutldata()->setPosY(reference - be->ryoffset());
shift -= be->ldata()->pos().y();
for (EngravingItem* e : elements[s]) {
if ((above && e->placeBelow()) || (!above && e->placeAbove())) {
continue;
}
modified.push_back(e);
handled.push_back(e);
moved = true;
if (e != be) {
e->mutldata()->moveY(-shift);
}
}
for (auto e : handled) {
muse::remove(elements[s], e);
}
}
}
return moved;
}

void HarmonyList::addToSkyline(const System* system)
{
for (EngravingItem* e : modified) {
const Segment* s = toSegment(e->explicitParent());
const MeasureBase* m = toMeasureBase(s->explicitParent());
system->staff(e->staffIdx())->skyline().add(e->shape().translated(e->pos() + s->pos() + m->pos()));
if (!e->isFretDiagram()) {
continue;
}
FretDiagram* fd = toFretDiagram(e);
Harmony* h = fd->harmony();
if (h) {
system->staff(e->staffIdx())->skyline().add(h->shape().translated(h->pos() + fd->pos() + s->pos() + m->pos()));
} else {
system->staff(e->staffIdx())->skyline().add(fd->shape().translated(fd->pos() + s->pos() + m->pos()));
}
}
}
32 changes: 28 additions & 4 deletions src/engraving/rendering/score/harmonylayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_ENGRAVING_HARMONYLAYOUT_DEV_H
#define MU_ENGRAVING_HARMONYLAYOUT_DEV_H
#pragma once

#include <vector>

Expand All @@ -32,6 +31,33 @@ class System;
}

namespace mu::engraving::rendering::score {
// Help class.
// Contains harmonies/fretboard per segment.
class HarmonyList : public std::vector<EngravingItem*>
{
// muse::OBJECT_ALLOCATOR(mu::engraving, HarmonyList);

std::map<const Segment*, std::vector<EngravingItem*> > elements;
std::vector<EngravingItem*> modified;

EngravingItem* getReferenceElement(const Segment* s, bool above, bool visible) const;

public:
HarmonyList()
{
elements.clear();
modified.clear();
}

void append(const Segment* s, EngravingItem* e) { elements[s].push_back(e); }

double getReferenceHeight(bool above) const;

bool align(bool above, double reference, double maxShift);

void addToSkyline(const System* system);
};

class HarmonyLayout
{
public:
Expand All @@ -41,5 +67,3 @@ class HarmonyLayout
const double maxShiftBelow);
};
}

#endif // MU_ENGRAVING_HARMONYLAYOUT_DEV_H
Loading