void StemDetection::findStems() { cerr << "...Finding stems..." << endl; int nRows = vBoard.size(), nCols = vBoard[0].size(); for (int j = 0; j < nCols; j++) { for (int i = 0, len = 0, start = 0; i <= nRows; i++) { if (i == nRows || vBoard[i][j] == WHITE) { if (len >= 2 * staffSpaceHeight) { Stem stem; for (int k = start; k < i; k++) stem.AddPixel(Point2i(k, j)); if (isStem(stem)) stems.push_back(stem); } len = 0; start = i + 1; continue; } else len++; } } mergeStems(); filterStems(); }
//--------------------------------------------------------- // updateExample //--------------------------------------------------------- void EditDrumset::updateExample() { int pitch = pitchList->currentItem()->data(0, Qt::UserRole).toInt(); if (!nDrumset.isValid(pitch)) { drumNote->add(0, 0, ""); return; } int line = nDrumset.line(pitch); NoteHead::Group noteHead = nDrumset.noteHead(pitch); int voice = nDrumset.voice(pitch); Direction dir = nDrumset.stemDirection(pitch); bool up = (Direction::UP == dir) || (Direction::AUTO == dir && line > 4); Chord* chord = new Chord(gscore); chord->setDurationType(TDuration::DurationType::V_QUARTER); chord->setStemDirection(dir); chord->setTrack(voice); chord->setUp(up); Note* note = new Note(gscore); note->setParent(chord); note->setTrack(voice); note->setPitch(pitch); note->setTpcFromPitch(); note->setLine(line); note->setPos(0.0, gscore->spatium() * .5 * line); note->setHeadType(NoteHead::Type::HEAD_QUARTER); note->setHeadGroup(noteHead); note->setCachedNoteheadSym(Sym::name2id(quarterCmb->currentData().toString())); chord->add(note); Stem* stem = new Stem(gscore); stem->setLen((up ? -3.0 : 3.0) * gscore->spatium()); chord->add(stem); drumNote->add(0, chord, qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData())); }
void DrumTools::setDrumset(Score* s, Staff* st, Drumset* ds) { if (s == _score && staff == st && drumset == ds) return; _score = s; staff = st; drumset = ds; drumPalette->clear(); if (drumset == 0) return; int drumInstruments = 0; for (int pitch = 0; pitch < 128; ++pitch) { if (drumset->isValid(pitch)) ++drumInstruments; } int i = 0; double _spatium = gscore->spatium(); for (int pitch = 0; pitch < 128; ++pitch) { if (!ds->isValid(pitch)) continue; bool up; int line = ds->line(pitch); int noteHead = ds->noteHead(pitch); int voice = ds->voice(pitch); Direction dir = ds->stemDirection(pitch); if (dir == UP) up = true; else if (dir == DOWN) up = false; else up = line > 4; Chord* chord = new Chord(gscore); chord->setDurationType(Duration::V_QUARTER); chord->setStemDirection(dir); chord->setTrack(voice); Note* note = new Note(gscore); note->setParent(chord); note->setTrack(voice); note->setPitch(pitch); note->setTpcFromPitch(); note->setLine(line); note->setPos(0.0, _spatium * .5 * line); note->setHeadGroup(noteHead); chord->add(note); Stem* stem = new Stem(gscore); stem->setLen((up ? -3.0 : 3.0) * _spatium); chord->setStem(stem); stem->setPos(note->stemPos(up)); int sc = drumset->shortcut(pitch); QString shortcut; if (sc) shortcut = QChar(sc); drumPalette->append(chord, qApp->translate("drumset", qPrintable(drumset->name(pitch))), shortcut); ++i; } }
void DrumTools::updateDrumset(const Drumset* ds) { drumPalette->clear(); drumset = ds; if (!drumset) return; double _spatium = gscore->spatium(); for (int pitch = 0; pitch < 128; ++pitch) { if (!drumset->isValid(pitch)) continue; bool up; int line = drumset->line(pitch); NoteHead::Group noteHead = drumset->noteHead(pitch); int voice = drumset->voice(pitch); Direction dir = drumset->stemDirection(pitch); if (dir == Direction::UP) up = true; else if (dir == Direction::DOWN) up = false; else up = line > 4; Chord* chord = new Chord(gscore); chord->setDurationType(TDuration::DurationType::V_QUARTER); chord->setStemDirection(dir); chord->setUp(up); chord->setTrack(voice); Stem* stem = new Stem(gscore); stem->setLen((up ? -3.0 : 3.0) * _spatium); chord->add(stem); Note* note = new Note(gscore); note->setMark(true); note->setParent(chord); note->setTrack(voice); note->setPitch(pitch); note->setTpcFromPitch(); note->setLine(line); note->setPos(0.0, _spatium * .5 * line); note->setHeadGroup(noteHead); SymId noteheadSym = SymId::noteheadBlack; if (noteHead == NoteHead::Group::HEAD_CUSTOM) noteheadSym = drumset->noteHeads(pitch, NoteHead::Type::HEAD_QUARTER); else noteheadSym = note->noteHead(true, noteHead, NoteHead::Type::HEAD_QUARTER); note->setCachedNoteheadSym(noteheadSym); // we use the cached notehead so we don't recompute it at each layout chord->add(note); int sc = drumset->shortcut(pitch); QString shortcut; if (sc) shortcut = QChar(sc); drumPalette->append(chord, qApp->translate("drumset", drumset->name(pitch).toUtf8().data()), shortcut); } }
void DrumTools::updateDrumset(const Drumset* ds) { drumPalette->clear(); drumset = ds; if (!drumset) return; double _spatium = gscore->spatium(); for (int pitch = 0; pitch < 128; ++pitch) { if (!drumset->isValid(pitch)) continue; bool up; int line = drumset->line(pitch); NoteHead::Group noteHead = drumset->noteHead(pitch); int voice = drumset->voice(pitch); Direction dir = drumset->stemDirection(pitch); if (dir == Direction::UP) up = true; else if (dir == Direction::DOWN) up = false; else up = line > 4; Chord* chord = new Chord(gscore); chord->setDurationType(TDuration::DurationType::V_QUARTER); chord->setStemDirection(dir); chord->setUp(up); chord->setTrack(voice); Note* note = new Note(gscore); note->setParent(chord); note->setTrack(voice); note->setPitch(pitch); note->setTpcFromPitch(); note->setLine(line); note->setPos(0.0, _spatium * .5 * line); note->setHeadGroup(noteHead); chord->add(note); Stem* stem = new Stem(gscore); stem->setLen((up ? -3.0 : 3.0) * _spatium); chord->add(stem); stem->setPos(chord->stemPos()); int sc = drumset->shortcut(pitch); QString shortcut; if (sc) shortcut = QChar(sc); drumPalette->append(chord, qApp->translate("drumset", drumset->name(pitch).toLatin1().data()), shortcut); } }
void EditDrumset::updateExample() { int pitch = pitchList->currentItem()->data(0, Qt::UserRole).toInt(); if (!nDrumset.isValid(pitch)) { drumNote->add(0, 0, ""); return; } int line = nDrumset.line(pitch); NoteHead::Group noteHead = nDrumset.noteHead(pitch); int voice = nDrumset.voice(pitch); MScore::Direction dir = nDrumset.stemDirection(pitch); bool up; if (dir == MScore::Direction::UP) up = true; else if (dir == MScore::Direction::DOWN) up = false; else up = line > 4; Chord* chord = new Chord(gscore); chord->setDurationType(TDuration::DurationType::V_QUARTER); chord->setStemDirection(dir); chord->setTrack(voice); chord->setUp(up); Note* note = new Note(gscore); note->setParent(chord); note->setTrack(voice); note->setPitch(pitch); note->setTpcFromPitch(); note->setLine(line); note->setPos(0.0, gscore->spatium() * .5 * line); note->setHeadGroup(noteHead); chord->add(note); Stem* stem = new Stem(gscore); stem->setLen((up ? -3.0 : 3.0) * gscore->spatium()); chord->add(stem); stem->setPos(chord->stemPos()); drumNote->add(0, chord, qApp->translate("drumset", nDrumset.name(pitch).toUtf8().constData())); }
void Tremolo::layout() { qreal _spatium = spatium(); qreal w2 = _spatium * score()->styleS(ST_tremoloWidth).val() * .5; qreal h2 = _spatium * score()->styleS(ST_tremoloBoxHeight).val() * .5; qreal lw = _spatium * score()->styleS(ST_tremoloStrokeWidth).val(); qreal td = _spatium * score()->styleS(ST_tremoloDistance).val(); path = QPainterPath(); qreal ty = 0.0; for (int i = 0; i < _lines; ++i) { path.moveTo(-w2, ty + h2 - lw); path.lineTo( w2, ty - h2); path.lineTo( w2, ty - h2 + lw); path.lineTo(-w2, ty + h2); path.closeSubpath(); ty += td; } QRectF rect = path.boundingRect(); if ((parent() == 0) && !twoNotes()) rect.setHeight(rect.height() + _spatium); setbbox(rect); _chord1 = static_cast<Chord*>(parent()); if (_chord1 == 0) return; Note* anchor1 = _chord1->upNote(); Stem* stem = _chord1->stem(); qreal x, y, h; if (stem) { x = stem->pos().x(); y = stem->pos().y(); h = stem->stemLen(); } else { // center tremolo above note x = anchor1->x() + anchor1->headWidth() * .5; y = anchor1->y(); h = 2.0 * _spatium + bbox().height(); if (anchor1->line() > 4) h *= -1; } if (!twoNotes()) { // // single note tremolos // bool up = _chord1->up(); int line = up ? _chord1->upLine() : _chord1->downLine(); static const qreal t[3][2][4][2] = { // normal stem { // DOWN { // even line odd line { 6, 5 }, // line 1 { 6 - 2 * .8, 5 - 2 * .8 }, // line 2 { 6 - 4 * .8, 3 }, // line 3 { 2 , 3 } // line 4 }, // UP { // even line odd line { -6, -5 }, // line 1 { -6, -5 }, // line 2 { -6, -3 - 4 * .8 }, // line 3 { -2 - 6 * .8, -3 - 6 * .8 } // line 4 } }, // stem with hook { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, // stem with beam { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, }; int idx = _chord1->hook() ? 1 : (_chord1->beam() ? 2 : 0); y = (line + t[idx][up][_lines-1][line & 1]) * _spatium * .5; setPos(x, y); return; } y += (h - bbox().height()) * .5; // // two chord tremolo // Segment* s = _chord1->segment()->next(); while (s) { if (s->element(track()) && (s->element(track())->type() == CHORD)) break; s = s->next(); } if (s == 0) { qDebug("no second note of tremolo found"); return; } _chord2 = static_cast<Chord*>(s->element(track())); _chord2->setTremolo(this); int x2 = _chord2->stemPosBeam().x(); int x1 = _chord1->stemPosBeam().x(); // qreal x2 = _chord2->_chord2->up()stemPos(_chord2->up(), true).x(); // qreal x1 = _chord1->stemPos(_chord1->up(), true).x(); x = x1 - _chord1->pagePos().x() + (x2 - x1 + _chord1->upNote()->headWidth()) * .5; setPos(x, y); }
void Fingering::layout() { if (parent()) { Fraction tick = parent()->tick(); const Staff* st = staff(); if (st && st->isTabStaff(tick) && !st->staffType(tick)->showTabFingering()) { setbbox(QRectF()); return; } } TextBase::layout(); rypos() = 0.0; // handle placement below if (autoplace() && note()) { Note* n = note(); Chord* chord = n->chord(); bool voices = chord->measure()->hasVoices(chord->staffIdx()); bool tight = voices && chord->notes().size() == 1 && !chord->beam() && tid() != Tid::STRING_NUMBER; qreal headWidth = n->bboxRightPos(); // update offset after drag qreal rebase = 0.0; if (offsetChanged() != OffsetChange::NONE) rebase = rebaseOffset(); // temporarily exclude self from chord shape setAutoplace(false); if (layoutType() == ElementType::CHORD) { Stem* stem = chord->stem(); Segment* s = chord->segment(); Measure* m = s->measure(); qreal sp = spatium(); qreal md = minDistance().val() * sp; SysStaff* ss = m->system()->staff(chord->vStaffIdx()); Staff* vStaff = chord->staff(); // TODO: use current height at tick if (n->mirror()) rxpos() -= n->ipos().x(); rxpos() += headWidth * .5; if (placeAbove()) { if (tight) { if (chord->stem()) rxpos() -= 0.8 * sp; rypos() -= 1.5 * sp; } else { QRectF r = bbox().translated(m->pos() + s->pos() + chord->pos() + n->pos() + pos()); SkylineLine sk(false); sk.add(r.x(), r.bottom(), r.width()); qreal d = sk.minDistance(ss->skyline().north()); qreal yd = 0.0; if (d > 0.0 && isStyled(Pid::MIN_DISTANCE)) yd -= d + height() * .25; // force extra space above staff & chord (but not other fingerings) qreal top; if (chord->up() && chord->beam() && stem) { top = stem->y() + stem->bbox().top(); } else { Note* un = chord->upNote(); top = qMin(0.0, un->y() + un->bbox().top()); } top -= md; qreal diff = (bbox().bottom() + ipos().y() + yd + n->y()) - top; if (diff > 0.0) yd -= diff; if (offsetChanged() != OffsetChange::NONE) { // user moved element within the skyline // we may need to adjust minDistance, yd, and/or offset bool inStaff = placeAbove() ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height(); rebaseMinDistance(md, yd, sp, rebase, inStaff); } rypos() += yd; } } else { if (tight) { if (chord->stem()) rxpos() += 0.8 * sp; rypos() += 1.5 * sp; } else { QRectF r = bbox().translated(m->pos() + s->pos() + chord->pos() + n->pos() + pos()); SkylineLine sk(true); sk.add(r.x(), r.top(), r.width()); qreal d = ss->skyline().south().minDistance(sk); qreal yd = 0.0; if (d > 0.0 && isStyled(Pid::MIN_DISTANCE)) yd += d + height() * .25; // force extra space below staff & chord (but not other fingerings) qreal bottom; if (!chord->up() && chord->beam() && stem) { bottom = stem->y() + stem->bbox().bottom(); } else { Note* dn = chord->downNote(); bottom = qMax(vStaff->height(), dn->y() + dn->bbox().bottom()); } bottom += md; qreal diff = bottom - (bbox().top() + ipos().y() + yd + n->y()); if (diff > 0.0) yd += diff; if (offsetChanged() != OffsetChange::NONE) { // user moved element within the skyline // we may need to adjust minDistance, yd, and/or offset bool inStaff = placeAbove() ? r.bottom() + rebase > 0.0 : r.top() + rebase < staff()->height(); rebaseMinDistance(md, yd, sp, rebase, inStaff); } rypos() += yd; } } } else if (tid() == Tid::LH_GUITAR_FINGERING) { // place to left of note qreal left = n->shape().left(); if (left - n->x() > 0.0) rxpos() -= left; else rxpos() -= n->x(); } // for other fingering styles, do not autoplace // restore autoplace setAutoplace(true); } else if (offsetChanged() != OffsetChange::NONE) { // rebase horizontally too, as autoplace may have adjusted it rebaseOffset(false); } setOffsetChanged(false); }
void Tremolo::layout() { qreal _spatium = spatium() * mag(); qreal w2 = _spatium * score()->styleS(StyleIdx::tremoloWidth).val() * .5; qreal lw = _spatium * score()->styleS(StyleIdx::tremoloStrokeWidth).val(); qreal td = _spatium * score()->styleS(StyleIdx::tremoloDistance).val(); path = QPainterPath(); qreal ty = 0.0; for (int i = 0; i < _lines; i++) { path.addRect(-w2, ty, 2.0 * w2, lw); ty += td; } // QRectF rect = path.boundingRect(); // if ((parent() == 0) && !twoNotes()) // rect.setHeight(rect.height() + _spatium); _chord1 = static_cast<Chord*>(parent()); if (_chord1 == 0) { // just for the palette QTransform shearTransform; shearTransform.shear(0.0, -(lw / 2.0) / w2); path = shearTransform.map(path); setbbox(path.boundingRect()); addbbox(QRectF(bbox().x(), bbox().bottom(), bbox().width(), _spatium)); return; } Note* anchor1 = _chord1->upNote(); Stem* stem = _chord1->stem(); qreal x, y, h; if (stem) { x = stem->pos().x(); y = stem->pos().y(); h = stem->stemLen(); } else { // center tremolo above note x = anchor1->x() + anchor1->headWidth() * .5; y = anchor1->y(); h = 2.0 * _spatium + bbox().height(); if (anchor1->line() > 4) h *= -1; } if (!twoNotes()) { // // single note tremolos // bool up = _chord1->up(); int line = up ? _chord1->upLine() : _chord1->downLine(); static const qreal t[3][2][4][2] = { // normal stem { // DOWN { // even line odd line { 6, 5 }, // line 1 { 6 - 2 * .8, 5 - 2 * .8 }, // line 2 { 6 - 4 * .8, 3 }, // line 3 { 2 , 3 } // line 4 }, // UP { // even line odd line { -6, -5 }, // line 1 { -6, -5 }, // line 2 { -6, -3 - 4 * .8 }, // line 3 { -2 - 6 * .8, -3 - 6 * .8 } // line 4 } }, // stem with hook { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, // stem with beam { // DOWN { // even line odd line { 3, 3 }, // line 1 { 2, 2 }, // line 2 { 2, 2 }, // line 3 { 2, 2 } // line 4 }, // UP { // even line odd line { -3, -3 }, // line 1 { -2 - 2 * .8, -2 - 2 * .8 }, // line 2 { -2 - 4 * .8, -2 - 4 * .8 }, // line 3 { -2 - 6 * .8, -2 - 6 * .8 } // line 4 } }, }; int idx = _chord1->hook() ? 1 : (_chord1->beam() ? 2 : 0); y = (line + t[idx][up][_lines-1][line & 1]) * spatium() * .5; QTransform shearTransform; shearTransform.shear(0.0, -(lw / 2.0) / w2); path = shearTransform.map(path); setbbox(path.boundingRect()); setPos(x, y); adjustReadPos(); return; } y += (h - bbox().height()) * .5; // // two chord tremolo // Segment* s = _chord1->segment()->next(); while (s) { if (s->element(track()) && (s->element(track())->type() == Element::Type::CHORD)) break; s = s->next(); } if (s == 0) { qDebug("no second note of tremolo found"); return; } _chord2 = static_cast<Chord*>(s->element(track())); _chord2->setTremolo(this); Stem* stem1 = _chord1->stem(); Stem* stem2 = _chord2->stem(); // compute the y coordinates of the tips of the stems qreal y1, y2; qreal firstChordStaffY; if (stem2 && stem1) { // stemPageYOffset variable is used for the case when the first // chord is cross-staff firstChordStaffY = stem1->pagePos().y() - stem1->y(); // y coordinate of the staff of the first chord y1 = stem1->y() + stem1->p2().y(); y2 = stem2->pagePos().y() - firstChordStaffY + stem2->p2().y(); // ->p2().y() is better than ->stemLen() } else { firstChordStaffY = _chord1->pagePos().y() - _chord1->y(); // y coordinate of the staff of the first chord y1 = _chord1->stemPosBeam().y() - firstChordStaffY + _chord1->defaultStemLength(); y2 = _chord2->stemPosBeam().y() - firstChordStaffY + _chord2->defaultStemLength(); } // improve the case when one stem is up and another is down if (_chord1->beams() == 0 && _chord2->beams() == 0 && _chord1->up() != _chord2->up()) { qreal meanNote1Y = .5 * (_chord1->upNote()->pagePos().y() - firstChordStaffY + _chord1->downNote()->pagePos().y() - firstChordStaffY); qreal meanNote2Y = .5 * (_chord2->upNote()->pagePos().y() - firstChordStaffY + _chord2->downNote()->pagePos().y() - firstChordStaffY); y1 = .5 * (y1 + meanNote1Y); y2 = .5 * (y2 + meanNote2Y); } y = (y1 + y2) * .5; if (!_chord1->up()) { y -= path.boundingRect().height() * .5; } if (!_chord2->up()) { y -= path.boundingRect().height() * .5; } // compute the x coordinates of the inner edge of the stems qreal x2 = _chord2->stemPosBeam().x(); if (_chord2->up() && stem2) x2 -= stem2->lineWidth(); qreal x1 = _chord1->stemPosBeam().x(); if (!_chord1->up() && stem1) x1 += stem1->lineWidth(); x = (x1 + x2) * .5 - _chord1->pagePos().x(); QTransform xScaleTransform; // TODO const qreal H_MULTIPLIER = score()->styleS(StyleIdx::tremoloBeamLengthMultiplier).val(); const qreal H_MULTIPLIER = 0.62; // TODO const qreal MAX_H_LENGTH = _spatium * score()->styleS(StyleIdx::tremoloBeamLengthMultiplier).val(); const qreal MAX_H_LENGTH = _spatium * 12.0; qreal xScaleFactor = qMin(H_MULTIPLIER * (x2 - x1), MAX_H_LENGTH); xScaleFactor /= (2.0 * w2); xScaleTransform.scale(xScaleFactor, 1.0); path = xScaleTransform.map(path); qreal beamYOffset = 0.0; if (_chord1->beams() == _chord2->beams() && _chord1->beam()) { int beams = _chord1->beams(); qreal beamHalfLineWidth = point(score()->styleS(StyleIdx::beamWidth)) * .5 * mag(); beamYOffset = beams * _chord1->beam()->beamDist() - beamHalfLineWidth; if (_chord1->up() != _chord2->up()) { // cross-staff beamYOffset += beamYOffset + beamHalfLineWidth; } else if (!_chord1->up() && !_chord2->up()) { beamYOffset = -beamYOffset; } } QTransform shearTransform; if (_chord1->beams() == 0 && _chord2->beams() == 0) { if (_chord1->up() && !_chord2->up()) shearTransform.shear(0.0, (y2 - y1 - path.boundingRect().height()) / (x2 - x1)); else if (!_chord1->up() && _chord2->up()) shearTransform.shear(0.0, (y2 - y1 + path.boundingRect().height()) / (x2 - x1)); else shearTransform.shear(0.0, (y2 - y1) / (x2 - x1)); } else { shearTransform.shear(0.0, (y2 - y1) / (x2 - x1)); } path = shearTransform.map(path); setbbox(path.boundingRect()); setPos(x, y + beamYOffset); adjustReadPos(); }
bool StemDetection::isStem(Stem & stem) { int nRows = vBoard.size(), nCols = vBoard[0].size(); Point2i minX = stem.GetMinX(), maxX = stem.GetMaxX(); int height = stem.GetHeight(); for (int x = minX.x; x <= min(minX.x + height / 4, maxX.x); x++) { int dist = maximumCanGo(x, minX.y, staffLineHeight); if (dist >= 3 * staffLineHeight) return true; /*int tolerance = staffLineHeight; int xLeft = minX.y, xRight = minX.y; while (xLeft >= 0) { if (vBoard[x][xLeft] == BLACK) xLeft--; else if (tolerance > 0) { xLeft--; tolerance--; } else break; } xLeft++; while (xRight < nCols) { if (vBoard[x][xRight] == BLACK) xRight++; else if (tolerance > 0) { xRight++; tolerance--; } else break; } xRight--; if (xRight - xLeft + 1 >= 3 * staffLineHeight) return true;*/ } for (int x = maxX.x; x >= max(maxX.x - height / 4, minX.x); x--) { int dist = maximumCanGo(x, maxX.y, staffLineHeight); if (dist >= 3 * staffLineHeight) return true; /*int tolerance = staffLineHeight; int xLeft = maxX.y, xRight = maxX.y; while (xLeft >= 0) { if (vBoard[x][xLeft] == BLACK) xLeft--; else if (tolerance > 0) { xLeft--; tolerance--; } else break; } xLeft++; while (xRight < nCols) { if (vBoard[x][xRight] == BLACK) xRight++; else if (tolerance > 0) { xRight++; tolerance--; } else break; } xRight--; if (xRight - xLeft + 1 >= 3 * staffLineHeight) return true;*/ } return false; }
void ChordRest::layoutArticulations() { if (parent() == 0 || _articulations.isEmpty()) return; qreal _spatium = spatium(); qreal _spStaff = _spatium * staff()->lineDistance(); // scaled to staff line distance for vert. pos. within a staff if (type() == Element::Type::CHORD) { if (_articulations.size() == 1) { static_cast<Chord*>(this)->layoutArticulation(_articulations[0]); return; } if (_articulations.size() == 2) { // // staccato | tenuto + marcato // Articulation* a1 = _articulations[0]; Articulation* a2 = _articulations[1]; ArticulationType st1 = a1->articulationType(); ArticulationType st2 = a2->articulationType(); if ((st2 == ArticulationType::Tenuto || st2 == ArticulationType::Staccato) && (st1 == ArticulationType::Marcato)) { qSwap(a1, a2); qSwap(st1, st2); } if ((st1 == ArticulationType::Tenuto || st1 == ArticulationType::Staccato) && (st2 == ArticulationType::Marcato)) { QPointF pt = static_cast<Chord*>(this)->layoutArticulation(a1); pt.ry() += a1->up() ? -_spStaff * .5 : _spStaff * .5; a2->layout(); a2->setUp(a1->up()); a2->setPos(pt); a2->adjustReadPos(); return; } // // staccato | tenuto + sforzato // if ((st2 == ArticulationType::Tenuto || st2 == ArticulationType::Staccato) && (st1 == ArticulationType::Sforzatoaccent)) { qSwap(a1, a2); qSwap(st1, st2); } if ((st1 == ArticulationType::Tenuto || st1 == ArticulationType::Staccato) && (st2 == ArticulationType::Sforzatoaccent)) { QPointF pt = static_cast<Chord*>(this)->layoutArticulation(a1); pt.ry() += a1->up() ? -_spStaff * .7 : _spStaff * .7; a2->layout(); a2->setUp(a1->up()); a2->setPos(pt); a2->adjustReadPos(); return; } } } qreal x = centerX(); qreal distance0 = score()->styleS(StyleIdx::propertyDistance).val() * _spatium; qreal distance1 = score()->styleS(StyleIdx::propertyDistanceHead).val() * _spatium; qreal distance2 = score()->styleS(StyleIdx::propertyDistanceStem).val() * _spatium; qreal chordTopY = upPos(); // note position of highest note qreal chordBotY = downPos(); // note position of lowest note qreal staffTopY = -distance2; qreal staffBotY = staff()->height() + distance2; // avoid collisions of staff articulations with chord notes: // gap between note and staff articulation is distance0 + 0.5 spatium if (type() == Element::Type::CHORD) { Chord* chord = static_cast<Chord*>(this); Stem* stem = chord->stem(); if (stem) { qreal y = stem->pos().y() + pos().y(); if (up() && stem->stemLen() < 0.0) y += stem->stemLen(); else if (!up() && stem->stemLen() > 0.0) y -= stem->stemLen(); if (beam()) { qreal bw = score()->styleS(StyleIdx::beamWidth).val() * _spatium; y += up() ? -bw : bw; } if (up()) staffTopY = qMin(staffTopY, qreal(y - 0.5 * _spatium)); else staffBotY = qMax(staffBotY, qreal(y + 0.5 * _spatium)); } } staffTopY = qMin(staffTopY, qreal(chordTopY - distance0 - 0.5 * _spatium)); staffBotY = qMax(staffBotY, qreal(chordBotY + distance0 + 0.5 * _spatium)); qreal dy = 0.0; int n = _articulations.size(); for (int i = 0; i < n; ++i) { Articulation* a = _articulations.at(i); // // determine MScore::Direction // if (a->direction() != MScore::Direction::AUTO) { a->setUp(a->direction() == MScore::Direction::UP); } else { if (a->anchor() == ArticulationAnchor::CHORD) a->setUp(!up()); else a->setUp(a->anchor() == ArticulationAnchor::TOP_STAFF || a->anchor() == ArticulationAnchor::TOP_CHORD); } } // // pass 1 // place tenuto and staccato // for (int i = 0; i < n; ++i) { Articulation* a = _articulations.at(i); a->layout(); ArticulationAnchor aa = a->anchor(); if ((a->articulationType() != ArticulationType::Tenuto) && (a->articulationType() != ArticulationType::Staccato)) continue; if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD) continue; bool bottom; if ((aa == ArticulationAnchor::CHORD) && measure()->hasVoices(a->staffIdx())) bottom = !up(); else bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up()); bool headSide = bottom == up(); dy += distance1; qreal y; Chord* chord = static_cast<Chord*>(this); if (bottom) { int line = downLine(); y = chordBotY + dy; if (!headSide && type() == Element::Type::CHORD && chord->stem()) { Stem* stem = chord->stem(); y = chordTopY + stem->stemLen(); if (chord->beam()) y += score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5; x = stem->pos().x(); int line = lrint((y+0.5*_spatium) / _spatium); if (line <= 4) // align between staff lines y = line * _spatium + _spatium * .5; else y += _spatium; } else { int lines = (staff()->lines() - 1) * 2; if (line < lines) y = (line & ~1) + 3; else y = line + 2; y *= _spatium * .5; } } else { int line = upLine(); y = chordTopY - dy; if (!headSide && type() == Element::Type::CHORD && chord->stem()) { Stem* stem = chord->stem(); y = chordBotY + stem->stemLen(); if (chord->beam()) y -= score()->styleS(StyleIdx::beamWidth).val() * _spatium * .5; x = stem->pos().x(); int line = lrint((y-0.5*_spatium) / _spatium); if (line >= 0) // align between staff lines y = line * _spatium - _spatium * .5; else y -= _spatium; } else { if (line > 0) y = ((line+1) & ~1) - 3; else y = line - 2; y *= _spatium * .5; } } dy += _spatium * .5; a->setPos(x, y); } // reserve space for slur bool botGap = false; bool topGap = false; #if 0 // TODO-S: optimize for (Spanner* sp = _spannerFor; sp; sp = sp->next()) { if (sp->type() != SLUR) continue; Slur* s = static_cast<Slur*>(sp); if (s->up()) topGap = true; else botGap = true; } for (Spanner* sp = _spannerBack; sp; sp = sp->next()) { if (sp->type() != SLUR) continue; Slur* s = static_cast<Slur*>(sp); if (s->up()) topGap = true; else botGap = true; } #endif if (botGap) chordBotY += _spatium; if (topGap) chordTopY -= _spatium; // // pass 2 // place all articulations with anchor at chord/rest // n = _articulations.size(); for (int i = 0; i < n; ++i) { Articulation* a = _articulations.at(i); a->layout(); ArticulationAnchor aa = a->anchor(); if ((a->articulationType() == ArticulationType::Tenuto) || (a->articulationType() == ArticulationType::Staccato)) continue; if (aa != ArticulationAnchor::CHORD && aa != ArticulationAnchor::TOP_CHORD && aa != ArticulationAnchor::BOTTOM_CHORD) continue; // for tenuto and staccate check for staff line collision bool staffLineCT = a->articulationType() == ArticulationType::Tenuto || a->articulationType() == ArticulationType::Staccato; // qreal sh = a->bbox().height() * mag(); bool bottom = (aa == ArticulationAnchor::BOTTOM_CHORD) || (aa == ArticulationAnchor::CHORD && up()); dy += distance1; if (bottom) { qreal y = chordBotY + dy; if (staffLineCT && (y <= staffBotY -.1 - dy)) { qreal l = y / _spatium; qreal delta = fabs(l - round(l)); if (delta < 0.4) { y += _spatium * .5; dy += _spatium * .5; } } a->setPos(x, y); // - a->bbox().y() + a->bbox().height() * .5); } else { qreal y = chordTopY - dy; if (staffLineCT && (y >= (staffTopY +.1 + dy))) { qreal l = y / _spatium; qreal delta = fabs(l - round(l)); if (delta < 0.4) { y -= _spatium * .5; dy += _spatium * .5; } } a->setPos(x, y); // + a->bbox().y() - a->bbox().height() * .5); } } // // pass 3 // now place all articulations with staff top or bottom anchor // qreal dyTop = staffTopY; qreal dyBot = staffBotY; /* if ((upPos() - _spatium) < dyTop) dyTop = upPos() - _spatium; if ((downPos() + _spatium) > dyBot) dyBot = downPos() + _spatium; */ for (int i = 0; i < n; ++i) { Articulation* a = _articulations.at(i); ArticulationAnchor aa = a->anchor(); if (aa == ArticulationAnchor::TOP_STAFF || aa == ArticulationAnchor::BOTTOM_STAFF) { if (a->up()) { a->setPos(x, dyTop); dyTop -= distance0; } else { a->setPos(x, dyBot); dyBot += distance0; } } a->adjustReadPos(); } }