Skip to content

Commit

Permalink
Manually merged #6321 to master
Browse files Browse the repository at this point in the history
  • Loading branch information
anatoly-os committed Aug 11, 2020
1 parent d126f5f commit 3cc24c3
Showing 1 changed file with 137 additions and 58 deletions.
195 changes: 137 additions & 58 deletions libmscore/layout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3484,17 +3484,145 @@ bool inline almostZero(qreal value)
void alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool harmony, const qreal maxShiftAbove,
const qreal maxShiftBelow)
{
// Help class.
// Contains harmonies/fretboard per segment.
class HarmonyList : public QList<Element*>
{
QMap<const Segment*, QList<Element*> > elements;
QList<Element*> modified;

Element* 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.
Element* element { nullptr };
for (Element* e : elements[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, Element* e)
{
elements[s].append(e);
}

qreal 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 };
qreal ref { 0.0 };
for (auto s : elements.keys()) {
Element* e { getReferenceElement(s, above, true) };
if (!e) {
continue;
}
if (e->placeAbove() && above) {
ref = first ? e->y() : qMin(ref, e->y());
first = false;
} else if (e->placeBelow() && !above) {
ref = first ? e->y() : qMax(ref, e->y());
first = false;
}
}
return ref;
}

bool align(bool above, qreal reference, qreal 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 (almostZero(reference)) {
return moved;
}

for (auto s : elements.keys()) {
QList<Element*> handled;
Element* 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)))))) {
qreal shift = be->rypos();
be->rypos() = reference - be->ryoffset();
shift -= be->rypos();
for (Element* e : elements[s]) {
if ((above && e->placeBelow()) || (!above && e->placeAbove())) {
continue;
}
modified.append(e);
handled.append(e);
moved = true;
if (e != be) {
e->rypos() -= shift;
}
}
for (auto e : handled) {
elements[s].removeOne(e);
}
}
}
return moved;
}

void addToSkyline(const System* system)
{
for (Element* e : modified) {
const Segment* s = toSegment(e->parent());
const MeasureBase* m = toMeasureBase(s->parent());
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 (almostZero(maxShiftAbove) && almostZero(maxShiftBelow)) {
return;
}

// Collect all fret diagrams and chord symbol and store them per staff.
// In the same pass, the maximum height is collected.
QMap<int, QList<Element*> > staves;
QMap<int, HarmonyList> staves;
for (const Segment* s : sl) {
for (Element* e : s->annotations()) {
if ((harmony && e->isHarmony()) || (!harmony && e->isFretDiagram())) {
staves[e->staffIdx()].append(e);
staves[e->staffIdx()].append(s, e);
}
}
}
Expand All @@ -3505,65 +3633,16 @@ void alignHarmonies(const System* system, const std::vector<Segment*>& sl, bool
// - Find highest placed harmony/fretdiagram.
// - Align all harmony/fretdiagram objects placed between height and height-maxShiftAbove.
// - Repeat for all harmony/fretdiagram objects below heigt-maxShiftAbove.
QList<Element*> modified;
while (!staves[idx].isEmpty()) {
// Pass 1, find highest/lowest place harmony.
qreal referencePositionAbove { 0.0 };
qreal referencePositionBelow { 0.0 };
for (Element* e : staves[idx]) {
if (e->placeAbove() && (referencePositionAbove > e->y())) {
referencePositionAbove = e->y();
} else if (e->placeBelow() && (referencePositionBelow < e->y())) {
referencePositionBelow = e->y();
}
}

if (almostZero(referencePositionAbove) && almostZero(referencePositionBelow)) {
break;
}

// Pass 2, align all object placed between referencePosition and referencePosition-maxShiftAbove
QList<Element*> again;
while (!staves[idx].isEmpty()) {
Element* e = staves[idx].takeFirst();
if (e->placeAbove() && !almostZero(maxShiftAbove)) {
if (e->y() < (referencePositionAbove + maxShiftAbove)) {
e->rypos() = referencePositionAbove - e->ryoffset();
if (e->addToSkyline()) {
modified.append(e);
}
} else {
again.append(e);
}
} else if (e->placeBelow() && !almostZero(maxShiftBelow)) {
if (e->y() > (referencePositionBelow - maxShiftBelow)) {
e->rypos() = referencePositionBelow - e->ryoffset();
if (e->addToSkyline()) {
modified.append(e);
}
} else {
again.append(e);
}
}
}
staves[idx].append(again);
bool moved { true };
int pass { 0 };
while (moved && (pass++ < 10)) {
moved = false;
moved |= staves[idx].align(true, staves[idx].getReferenceHeight(true), maxShiftAbove);
moved |= staves[idx].align(false, staves[idx].getReferenceHeight(false), maxShiftBelow);
}

// Add all aligned objects to the sky line.
for (Element* e : modified) {
const Segment* s = toSegment(e->parent());
const MeasureBase* m = toMeasureBase(s->parent());
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()));
}
}
}
staves[idx].addToSkyline(system);
}
}

Expand Down

0 comments on commit 3cc24c3

Please sign in to comment.