fix #294085: all elements set to normal position if all rests in voices other than voice 1 are deleted

This factors out the inner workings of Measure::checkMultiVoices() into a variant of Measure::hasVoices() that takes a start tick and a length in addition to a staff index.

Co-authored-by: Howard-C <howardc@pku.edu.cn>
This commit is contained in:
Matt McClinch 2019-10-07 07:00:22 -04:00
parent 4708ea20fb
commit 472cb6bfb5
10 changed files with 56 additions and 51 deletions

View file

@ -396,7 +396,7 @@ void Beam::layout1()
Measure* m = c1->measure();
if (c1->stemDirection() != Direction::AUTO)
_up = c1->stemDirection() == Direction::UP;
else if (m->hasVoices(c1->staffIdx()))
else if (m->hasVoices(c1->staffIdx(), tick(), ticks()))
_up = !(c1->voice() % 2);
else if (!twoBeamedNotes()) {
// highest or lowest note determines stem direction
@ -483,7 +483,7 @@ void Beam::layoutGraceNotes()
ChordRest* cr = _elements[0];
Measure* m = cr->measure();
if (m->hasVoices(cr->staffIdx()))
if (m->hasVoices(cr->staffIdx(), tick(), ticks()))
_up = !(cr->voice() % 2);
else
_up = true;
@ -2493,6 +2493,19 @@ Fraction Beam::rtick() const
return _elements.empty() ? Fraction(0, 1) : _elements.front()->segment()->rtick();
}
//---------------------------------------------------------
// ticks
// calculate the ticks of all chords and rests connected by the beam
//---------------------------------------------------------
Fraction Beam::ticks() const
{
Fraction ticks = Fraction(0, 1);
for (ChordRest* cr : _elements)
ticks += cr->actualTicks();
return ticks;
}
//---------------------------------------------------------
// iconType
//---------------------------------------------------------

View file

@ -92,6 +92,7 @@ class Beam final : public Element {
Fraction tick() const override;
Fraction rtick() const override;
Fraction ticks() const;
void write(XmlWriter& xml) const override;
void read(XmlReader&) override;

View file

@ -868,7 +868,7 @@ void Chord::computeUp()
// if no stems or stem beside staves
if (tab->stemless() || !tab->stemThrough()) {
// if measure has voices, set stem direction according to voice
if (measure()->hasVoices(staffIdx()))
if (measure()->hasVoices(staffIdx(), tick(), actualTicks()))
_up = !(track() % 2);
else // if only voice 1,
// unconditionally set to down if not stems or according to TAB stem direction otherwise
@ -889,14 +889,14 @@ void Chord::computeUp()
//
// stem direction for grace notes
//
if (measure()->hasVoices(staffIdx()))
if (measure()->hasVoices(staffIdx(), tick(), actualTicks()))
_up = !(track() % 2);
else
_up = true;
}
else if (staffMove())
_up = staffMove() > 0;
else if (measure()->hasVoices(staffIdx()))
else if (measure()->hasVoices(staffIdx(), tick(), actualTicks()))
_up = !(track() % 2);
else {
int dnMaxLine = staff()->middleLine(tick());
@ -3401,7 +3401,7 @@ void Chord::layoutArticulations()
Articulation* prevArticulation = nullptr;
for (Articulation* a : _articulations) {
if (a->anchor() == ArticulationAnchor::CHORD) {
if (measure()->hasVoices(a->staffIdx()))
if (measure()->hasVoices(a->staffIdx(), tick(), actualTicks()))
a->setUp(up()); // if there are voices place articulation at stem
else if (a->symId() >= SymId::articMarcatoAbove && a->symId() <= SymId::articMarcatoTenutoBelow)
a->setUp(true); // Gould, p. 117: strong accents above staff

View file

@ -79,7 +79,7 @@ Placement Fingering::calculatePlacement() const
Staff* staff = chord->staff();
Part* part = staff->part();
int nstaves = part->nstaves();
bool voices = chord->measure()->hasVoices(staff->idx());
bool voices = chord->measure()->hasVoices(staff->idx(), chord->tick(), chord->actualTicks());
bool below = voices ? !chord->up() : (nstaves > 1) && (staff->rstaff() == nstaves - 1);
return below ? Placement::BELOW : Placement::ABOVE;
}
@ -105,7 +105,7 @@ void Fingering::layout()
if (autoplace() && note()) {
Note* n = note();
Chord* chord = n->chord();
bool voices = chord->measure()->hasVoices(chord->staffIdx());
bool voices = chord->measure()->hasVoices(chord->staffIdx(), chord->tick(), chord->actualTicks());
bool tight = voices && chord->notes().size() == 1 && !chord->beam() && tid() != Tid::STRING_NUMBER;
qreal headWidth = n->bboxRightPos();

View file

@ -128,7 +128,7 @@ static bool vUp(Chord* chord)
up = false;
else if (chord->track() > chord->beam()->track())
up = true;
else if (chord->measure()->hasVoices(chord->staffIdx()))
else if (chord->measure()->hasVoices(chord->staffIdx(), chord->tick(), chord->actualTicks()))
up = !(chord->track() % 2);
else
up = !chord->staff()->isTop();

View file

@ -2681,20 +2681,35 @@ void Measure::exchangeVoice(int strack, int dtrack, int staffIdx)
//---------------------------------------------------------
void Measure::checkMultiVoices(int staffIdx)
{
if (hasVoices(staffIdx, tick(), ticks()))
_mstaves[staffIdx]->setHasVoices(true);
else
_mstaves[staffIdx]->setHasVoices(false);
}
//---------------------------------------------------------
// hasVoices
//---------------------------------------------------------
bool Measure::hasVoices(int staffIdx, Fraction stick, Fraction len) const
{
int strack = staffIdx * VOICES + 1;
int etrack = staffIdx * VOICES + VOICES;
_mstaves[staffIdx]->setHasVoices(false);
Fraction etick = stick + len;
for (Segment* s = first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
if (s->tick() >= etick)
break;
for (int track = strack; track < etrack; ++track) {
Element* e = s->element(track);
if (e) {
bool v;
if (e->isChord()) {
v = false;
ChordRest* cr = toChordRest(s->element(track));
if (cr) {
if (cr->tick() + cr->actualTicks() <= stick)
continue;
bool v = false;
if (cr->isChord()) {
// consider chord visible if any note is visible
Chord* c = toChord(e);
Chord* c = toChord(cr);
for (Note* n : c->notes()) {
if (n->visible()) {
v = true;
@ -2702,15 +2717,14 @@ void Measure::checkMultiVoices(int staffIdx)
}
}
}
else
v = e->visible();
if (v) {
_mstaves[staffIdx]->setHasVoices(true);
return;
}
else if (cr->isRest())
v = cr->visible() && !toRest(cr)->isGap();
if (v)
return true;
}
}
}
return false;
}
//---------------------------------------------------------
@ -2878,27 +2892,6 @@ bool Measure::isOnlyDeletedRests(int track) const
return true;
}
//---------------------------------------------------------
// isOnlyDeletedRests
//---------------------------------------------------------
bool Measure::isOnlyDeletedRests(int track, const Fraction& stick, const Fraction& etick) const
{
static const SegmentType st { SegmentType::ChordRest };
for (const Segment* s = first(st); s; s = s->next(st)) {
if (s->segmentType() != st || !s->element(track))
continue;
ChordRest* cr = toChordRest(s->element(track));
if (cr->tick() + cr->globalTicks() <= stick)
continue;
if (cr->tick() >= etick)
return true;
if (!cr->isRest() || !toRest(cr)->isGap())
return false;
}
return true;
}
//---------------------------------------------------------
// stretchedLen
//---------------------------------------------------------

View file

@ -117,6 +117,7 @@ class Measure final : public MeasureBase {
void spatiumChanged(qreal oldValue, qreal newValue) override;
System* system() const { return (System*)parent(); }
bool hasVoices(int staffIdx, Fraction stick, Fraction len) const;
bool hasVoices(int staffIdx) const;
void setHasVoices(int staffIdx, bool v);
@ -227,7 +228,6 @@ class Measure final : public MeasureBase {
bool empty() const;
bool isOnlyRests(int track) const;
bool isOnlyDeletedRests(int track) const;
bool isOnlyDeletedRests(int track, const Fraction& stick, const Fraction& etick) const;
int playbackCount() const { return _playbackCount; }
void setPlaybackCount(int val) { _playbackCount = val; }

View file

@ -480,7 +480,7 @@ int Rest::getDotline(TDuration::DurationType durationType)
int Rest::computeLineOffset(int lines)
{
Segment* s = segment();
bool offsetVoices = s && measure() && measure()->hasVoices(staffIdx());
bool offsetVoices = s && measure() && measure()->hasVoices(staffIdx(), tick(), actualTicks());
if (offsetVoices && voice() == 0) {
// do not offset voice 1 rest if there exists a matching invisible rest in voice 2;
Element* e = s->element(track() + 1);
@ -490,8 +490,6 @@ int Rest::computeLineOffset(int lines)
offsetVoices = false;
}
}
else if (measure()->isOnlyDeletedRests(track() + 1, tick(), tick() + globalTicks()))
offsetVoices = false;
}
#if 0
if (offsetVoices && staff()->mergeMatchingRests()) {

View file

@ -1050,7 +1050,7 @@ SpannerSegment* Slur::layoutSystem(System* system)
// but grace notes are exceptions
_up = true;
}
else if (m1->hasVoices(startCR()->staffIdx()) && c1 && !c1->isGrace()) {
else if (m1->hasVoices(startCR()->staffIdx(), tick(), ticks()) && c1 && !c1->isGrace()) {
// in polyphonic passage, slurs go on the stem side
_up = startCR()->up();
}
@ -1172,7 +1172,7 @@ void Slur::layout()
// but grace notes are exceptions
_up = true;
}
else if (m1->hasVoices(startCR()->staffIdx()) && c1 && c1->noteType() == NoteType::NORMAL) {
else if (m1->hasVoices(startCR()->staffIdx(), tick(), ticks()) && c1 && c1->noteType() == NoteType::NORMAL) {
// in polyphonic passage, slurs go on the stem side
_up = startCR()->up();
}

View file

@ -544,7 +544,7 @@ void Tie::calculateDirection()
if (_slurDirection == Direction::AUTO) {
std::vector<Note*> notes = c1->notes();
size_t n = notes.size();
if (m1->hasVoices(c1->staffIdx()) || m2->hasVoices(c2->staffIdx())) {
if (m1->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks()) || m2->hasVoices(c2->staffIdx(), c2->tick(), c2->actualTicks())) {
// in polyphonic passage, ties go on the stem side
_up = c1->up();
}
@ -619,7 +619,7 @@ TieSegment* Tie::layoutFor(System* system)
}
Chord* c1 = startNote()->chord();
if (_slurDirection == Direction::AUTO) {
if (c1->measure()->hasVoices(c1->staffIdx())) {
if (c1->measure()->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks())) {
// in polyphonic passage, ties go on the stem side
_up = c1->up();
}