LCOV - code coverage report
Current view: top level - src/engraving/rendering/score - beamlayout.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 86.3 % 863 745
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2023 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "beamlayout.h"
      23              : 
      24              : #include <unordered_map>
      25              : #include <set>
      26              : 
      27              : #include "containers.h"
      28              : 
      29              : #include "dom/beam.h"
      30              : #include "dom/drumset.h"
      31              : #include "dom/part.h"
      32              : #include "dom/tremolotwochord.h"
      33              : #include "dom/tremolosinglechord.h"
      34              : #include "dom/chord.h"
      35              : #include "dom/factory.h"
      36              : #include "dom/measure.h"
      37              : #include "dom/rest.h"
      38              : #include "dom/score.h"
      39              : #include "dom/segment.h"
      40              : #include "dom/staff.h"
      41              : #include "dom/system.h"
      42              : #include "dom/timesig.h"
      43              : #include "dom/tuplet.h"
      44              : #include "dom/utils.h"
      45              : #include "dom/note.h"
      46              : 
      47              : #include "layoutcontext.h"
      48              : #include "tlayout.h"
      49              : #include "chordlayout.h"
      50              : #include "beamtremololayout.h"
      51              : #include "tremololayout.h"
      52              : #include "restlayout.h"
      53              : 
      54              : #include "log.h"
      55              : 
      56              : using namespace mu::engraving;
      57              : using namespace mu::engraving::rendering::score;
      58              : 
      59         8778 : void BeamLayout::layout(Beam* item, const LayoutContext& ctx)
      60              : {
      61         8788 :     TRACEFUNC;
      62              : 
      63         8778 :     Beam::LayoutData* ldata = item->mutldata();
      64              :     // all of the beam layout code depends on _elements being in order by tick
      65              :     // this may not be the case if two cr's were recently swapped.
      66         8778 :     std::sort(item->elements().begin(), item->elements().end(),
      67        42550 :               [](const ChordRest* a, const ChordRest* b) -> bool {
      68        42550 :         return a->tick() < b->tick();
      69              :     });
      70         8778 :     System* system = item->system();
      71              : 
      72         8778 :     std::vector<ChordRest*> crl;
      73              : 
      74         8778 :     size_t n = 0;
      75        38651 :     for (ChordRest* cr : item->elements()) {
      76        29873 :         auto newSystem = cr->measure()->system();
      77        29873 :         if (newSystem && newSystem != system) {
      78              :             SpannerSegmentType st;
      79            0 :             if (n == 0) {
      80            0 :                 st = SpannerSegmentType::BEGIN;
      81              :             } else {
      82            0 :                 st = SpannerSegmentType::MIDDLE;
      83              :             }
      84            0 :             ++n;
      85            0 :             if (item->beamFragments().size() < n) {
      86            0 :                 item->beamFragments().push_back(new BeamFragment);
      87              :             }
      88            0 :             layout2(item, ctx, crl, st, static_cast<int>(n) - 1);
      89            0 :             crl.clear();
      90            0 :             system = cr->measure()->system();
      91              :         }
      92        29873 :         crl.push_back(cr);
      93              :     }
      94              : 
      95         8778 :     Shape beamShape(Shape::Type::Composite);
      96              : 
      97         8778 :     if (!crl.empty()) {
      98              :         SpannerSegmentType st;
      99         8778 :         if (n == 0) {
     100         8778 :             st = SpannerSegmentType::SINGLE;
     101              :         } else {
     102            0 :             st = SpannerSegmentType::END;
     103              :         }
     104         8778 :         if (item->beamFragments().size() < (n + 1)) {
     105         2836 :             item->beamFragments().push_back(new BeamFragment);
     106              :         }
     107         8778 :         layout2(item, ctx, crl, st, static_cast<int>(n));
     108              : 
     109        21119 :         for (const BeamSegment* bs : item->beamSegments()) {
     110        12341 :             beamShape.add(bs->shape());
     111              :         }
     112              :     }
     113              : 
     114         8778 :     ldata->setShape(beamShape);
     115              : 
     116              :     // The beam may have changed shape. one-note trems within this beam need to be layed out here
     117        38651 :     for (ChordRest* cr : item->elements()) {
     118        29873 :         ChordLayout::computeUp(cr, ctx);
     119        29873 :         if (cr->isChord() && toChord(cr)->tremoloSingleChord()) {
     120          142 :             TremoloLayout::layout(toChord(cr)->tremoloSingleChord(), ctx);
     121              :         }
     122              :     }
     123         8778 : }
     124              : 
     125            0 : void BeamLayout::layoutIfNeed(Beam* item, const LayoutContext& ctx)
     126              : {
     127            0 :     if (!item->ldata()->isValid()) {
     128            0 :         BeamLayout::layout(item, ctx);
     129              :     }
     130            0 : }
     131              : 
     132              : //---------------------------------------------------------
     133              : //    - remove beam segments
     134              : //    - detach from system
     135              : //    - calculate stem direction and set chord
     136              : //---------------------------------------------------------
     137         4432 : void BeamLayout::layout1(Beam* item, LayoutContext& ctx)
     138              : {
     139         4432 :     Beam::LayoutData* ldata = item->mutldata();
     140              : 
     141         4432 :     const StaffType* staffType = item->staffType();
     142         4432 :     item->setTab((staffType && staffType->isTabStaff()) ? staffType : nullptr);
     143         4432 :     item->setIsBesideTabStaff(item->tab() && !item->tab()->stemless() && !item->tab()->stemThrough());
     144              : 
     145              :     // TAB's with stem beside staves have special layout
     146         4432 :     if (item->isBesideTabStaff()) {
     147            0 :         item->setUp(!item->tab()->stemsDown());
     148            0 :         item->setSlope(0.0);
     149            0 :         item->setCross(false);
     150            0 :         item->setMinMove(0);
     151            0 :         item->setMaxMove(0);
     152            0 :         for (ChordRest* cr : item->elements()) {
     153            0 :             if (cr->isChord()) {
     154            0 :                 item->setUp(cr->up());
     155            0 :                 break;
     156              :             }
     157              :         }
     158           86 :         return;
     159              :     }
     160              : 
     161         4432 :     if (item->staff()->isDrumStaff(item->tick())) {
     162           86 :         if (item->direction() != DirectionV::AUTO) {
     163           28 :             item->setUp(item->direction() == DirectionV::UP);
     164           58 :         } else if (item->isGrace()) {
     165            2 :             item->setUp(true);
     166              :         } else {
     167              :             // search through the beam for the first chord with explicit stem direction and use that.
     168              :             // if there is no explicit stem direction, default to the direction of the first stem.
     169           56 :             bool firstUp = false;
     170           56 :             bool firstChord = true;
     171           65 :             for (ChordRest* cr : item->elements()) {
     172           61 :                 if (!cr->isChord()) {
     173            0 :                     item->setUp(firstUp);
     174            0 :                     continue;
     175              :                 }
     176           61 :                 Chord* chord = toChord(cr);
     177           61 :                 DirectionV crDirection = toChord(cr)->stemDirection();
     178           61 :                 if (crDirection != DirectionV::AUTO) {
     179           52 :                     item->setUp(crDirection == DirectionV::UP);
     180           52 :                     break;
     181              :                 }
     182            9 :                 if (firstChord) {
     183            4 :                     const Staff* staff = item->staff();
     184            4 :                     const Part* part = staff ? staff->part() : nullptr;
     185            4 :                     const Drumset* ds = part ? part->instrument(item->tick())->drumset() : nullptr;
     186            4 :                     firstUp = ds ? ds->stemDirection(chord->upNote()->pitch()) == DirectionV::UP : chord->up();
     187            4 :                     firstChord = false;
     188              :                 }
     189            9 :                 item->setUp(firstUp);
     190              :             }
     191              :         }
     192          325 :         for (ChordRest* cr : item->elements()) {
     193          239 :             ChordLayout::computeUp(cr, ctx);
     194          239 :             if (cr->isChord()) {
     195          239 :                 ChordLayout::layoutStem(toChord(cr), ctx);
     196              :             }
     197              :         }
     198           86 :         return;
     199              :     }
     200              : 
     201         4346 :     item->setMinMove(std::numeric_limits<int>::max());
     202         4346 :     item->setMaxMove(std::numeric_limits<int>::min());
     203         4346 :     double mag = 0.0;
     204              : 
     205         4346 :     item->notePositions().clear();
     206         4346 :     staff_idx_t staffIdx = muse::nidx;
     207        19200 :     for (ChordRest* cr : item->elements()) {
     208        14854 :         double m = cr->isSmall() ? ctx.conf().styleD(Sid::smallNoteMag) : 1.0;
     209        14854 :         mag = std::max(mag, m);
     210        14854 :         if (cr->isChord()) {
     211        14817 :             Chord* chord = toChord(cr);
     212        14817 :             staffIdx = chord->vStaffIdx();
     213        14817 :             int i = chord->staffMove();
     214        14817 :             item->setMinMove(std::min(item->minCRMove(), i));
     215        14817 :             item->setMaxMove(std::max(item->maxCRMove(), i));
     216              : 
     217        31203 :             for (int distance : chord->noteDistances()) {
     218        16386 :                 item->notePositions().push_back(BeamBase::NotePosition(distance, cr->vStaffIdx()));
     219        14817 :             }
     220              :         }
     221              :     }
     222              : 
     223         4346 :     std::sort(item->notePositions().begin(), item->notePositions().end());
     224         4346 :     ldata->setMag(mag);
     225              : 
     226         4346 :     item->setCross(item->minCRMove() != item->maxCRMove());
     227              : 
     228              :     //
     229              :     // determine beam stem direction
     230              :     //
     231         4346 :     if (item->elements().empty()) {
     232            0 :         return;
     233              :     }
     234         4346 :     ChordRest* firstNote = item->elements().front();
     235         4346 :     Measure* measure = firstNote->measure();
     236         4346 :     bool hasMultipleVoices = measure->hasVoices(firstNote->staffIdx(), item->tick(), item->ticks());
     237         4346 :     if (computeUpForMovedCross(item)) {
     238         4346 :     } else if (item->direction() != DirectionV::AUTO) {
     239          986 :         item->setUp(item->direction() == DirectionV::UP);
     240         3360 :     } else if (item->maxCRMove() > 0) {
     241           17 :         item->setUp(false);
     242         3343 :     } else if (item->minCRMove() < 0) {
     243            4 :         item->setUp(true);
     244         3339 :     } else if (item->isGrace()) {
     245           86 :         if (hasMultipleVoices) {
     246            6 :             item->setUp(firstNote->track() % 2 == 0);
     247              :         } else {
     248           80 :             item->setUp(true);
     249              :         }
     250         3253 :     } else if (item->notePositions().size()) {
     251         3253 :         if (hasMultipleVoices) {
     252          589 :             item->setUp(firstNote->track() % 2 == 0);
     253              :         } else {
     254         2664 :             if (const Chord* chord = item->findChordWithCustomStemDirection()) {
     255           16 :                 item->setUp(chord->stemDirection() == DirectionV::UP);
     256              :             } else {
     257         2648 :                 std::set<BeamBase::NotePosition> notePosSet(item->notePositions().begin(), item->notePositions().end());
     258         2648 :                 std::vector<int> noteLines;
     259         2648 :                 noteLines.reserve(notePosSet.size());
     260         9456 :                 for (BeamBase::NotePosition pos : notePosSet) {
     261         6808 :                     noteLines.push_back(pos.line);
     262              :                 }
     263         2648 :                 item->setUp(ChordLayout::computeAutoStemDirection(noteLines) > 0);
     264         2648 :             }
     265              :         }
     266              :     } else {
     267            0 :         item->setUp(true);
     268              :     }
     269              : 
     270         4346 :     int middleStaffLine = firstNote->staffType()->middleLine();
     271        20732 :     for (size_t i = 0; i < item->notePositions().size(); i++) {
     272        16386 :         item->notePositions()[i].line += middleStaffLine;
     273              :     }
     274              : 
     275         4346 :     bool isEntirelyMoved = false;
     276         4346 :     if (item->minCRMove() == item->maxCRMove() && item->minCRMove() != 0) {
     277           45 :         isEntirelyMoved = true;
     278           45 :         item->setStaffIdx(staffIdx);
     279           45 :         if (item->direction() == DirectionV::AUTO) {
     280           13 :             item->setUp(item->maxCRMove() > 0);
     281              :         }
     282         4301 :     } else if (item->elements().size()) {
     283         4301 :         item->setStaffIdx(item->elements().at(0)->staffIdx());
     284              :     }
     285         4346 :     item->setFullCross(isEntirelyMoved);
     286              : 
     287         4346 :     item->setSlope(0.0);
     288              : 
     289        19200 :     for (ChordRest* cr : item->elements()) {
     290        14854 :         bool prevUp = cr->up();
     291        14854 :         ChordLayout::computeUp(cr, ctx);
     292        14854 :         if (cr->isChord() && cr->up() != prevUp) {
     293         1432 :             ChordLayout::layoutChords1(ctx, cr->segment(), cr->staffIdx());
     294         1432 :             cr->segment()->createShape(cr->staffIdx());
     295         1432 :             if (toChord(cr)->isGrace()) {
     296            0 :                 ChordLayout::layoutStem(toChord(cr), ctx);
     297              :             }
     298              :         }
     299              :     }
     300              : }
     301              : 
     302         8778 : void BeamLayout::layout2(Beam* item, const LayoutContext& ctx, const std::vector<ChordRest*>& chordRests, SpannerSegmentType, int frag)
     303              : {
     304         8788 :     TRACEFUNC;
     305              : 
     306         8778 :     BeamTremoloLayout::setupLData(item, item->mutldata(), ctx);
     307         8778 :     Chord* startChord = nullptr;
     308         8778 :     Chord* endChord = nullptr;
     309         8778 :     if (chordRests.empty()) {
     310            0 :         return;
     311              :     }
     312        38651 :     for (ChordRest* chordRest : chordRests) {
     313        29873 :         if (chordRest->isChord()) {
     314        29798 :             if (!startChord) {
     315         8778 :                 startChord = toChord(chordRest);
     316         8778 :                 endChord = startChord;
     317              :             } else {
     318        21020 :                 endChord = toChord(chordRest);
     319              :             }
     320        29798 :             ChordLayout::layoutStem(toChord(chordRest), ctx);
     321              :         }
     322              :     }
     323         8778 :     if (!startChord) {
     324              :         // we were passed a vector of only rests. we don't support beams across only rests
     325              :         // this beam will be deleted in LayoutBeams
     326            0 :         return;
     327              :     }
     328              : 
     329         8778 :     item->setBeamSpacing(ctx.conf().styleB(Sid::useWideBeams) ? 4 : 3);
     330         8778 :     item->setBeamDist((item->beamSpacing() / 4.0) * item->spatium() * item->mag());
     331         8778 :     item->setBeamWidth(item->absoluteFromSpatium(ctx.conf().styleS(Sid::beamWidth)) * item->mag());
     332              : 
     333         8778 :     item->setStartAnchor(BeamTremoloLayout::chordBeamAnchor(item->ldata(), startChord, ChordBeamAnchorType::Start));
     334         8778 :     item->setEndAnchor(BeamTremoloLayout::chordBeamAnchor(item->ldata(), endChord, ChordBeamAnchorType::End));
     335              : 
     336         8778 :     if (item->isGrace()) {
     337          276 :         item->setBeamDist(item->beamDist() * ctx.conf().styleD(Sid::graceNoteMag));
     338          276 :         item->setBeamWidth(item->beamWidth() * ctx.conf().styleD(Sid::graceNoteMag));
     339              :     }
     340              : 
     341         8778 :     int fragmentIndex = item->directionIdx();
     342         8778 :     if (item->userModified()) {
     343           23 :         BeamTremoloLayout::setupLData(item, item->mutldata(), ctx);
     344           23 :         double startY = item->beamFragments()[frag]->py1[fragmentIndex];
     345           23 :         double endY = item->beamFragments()[frag]->py2[fragmentIndex];
     346           23 :         if (ctx.conf().styleB(Sid::snapCustomBeamsToGrid)) {
     347           23 :             const double quarterSpace = item->spatium() / 4;
     348           23 :             startY = round(startY / quarterSpace) * quarterSpace;
     349           23 :             endY = round(endY / quarterSpace) * quarterSpace;
     350              :         }
     351           23 :         startY += item->pagePos().y();
     352           23 :         endY += item->pagePos().y();
     353           23 :         item->startAnchor().setY(startY);
     354           23 :         item->endAnchor().setY(endY);
     355           23 :         item->mutldata()->setAnchors(item->startAnchor(), item->endAnchor());
     356           23 :         item->computeAndSetSlope();
     357           23 :         createBeamSegments(item, ctx, chordRests);
     358           23 :         BeamLayout::setTremAnchors(item, ctx);
     359           23 :         return;
     360              :     }
     361              : 
     362              :     // anchor represents the middle of the beam, not the tip of the stem
     363              :     // location depends on _isBesideTabStaff
     364              : 
     365         8755 :     if (!item->isBesideTabStaff()) {
     366         8755 :         BeamTremoloLayout::setupLData(item, item->mutldata(), ctx);
     367         8755 :         BeamTremoloLayout::calculateAnchors(item, item->mutldata(), ctx, chordRests, item->notePositions());
     368         8755 :         item->setStartAnchor(item->ldata()->startAnchor);
     369         8755 :         item->setEndAnchor(item->ldata()->endAnchor);
     370         8755 :         item->computeAndSetSlope();
     371         8755 :         item->setBeamDist(item->ldata()->beamDist);
     372              :     } else {
     373            0 :         item->setSlope(0.0);
     374            0 :         Chord* startChord2 = nullptr;
     375            0 :         for (ChordRest* cr : chordRests) {
     376            0 :             if (cr->isChord()) {
     377            0 :                 startChord2 = toChord(cr);
     378            0 :                 break;
     379              :             }
     380              :         }
     381            0 :         BeamTremoloLayout::setupLData(item, item->mutldata(), ctx);
     382            0 :         double x1 = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), chordRests.front(), ChordBeamAnchorType::Start);
     383            0 :         double x2 = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), chordRests.back(), ChordBeamAnchorType::End);
     384            0 :         double y = BeamTremoloLayout::chordBeamAnchorY(item->ldata(), startChord2);
     385            0 :         item->startAnchor() = PointF(x1, y);
     386            0 :         item->endAnchor() = PointF(x2, y);
     387            0 :         item->mutldata()->setAnchors(item->startAnchor(), item->endAnchor());
     388              :     }
     389              : 
     390         8755 :     item->beamFragments()[frag]->py1[fragmentIndex] = item->startAnchor().y() - item->pagePos().y();
     391         8755 :     item->beamFragments()[frag]->py2[fragmentIndex] = item->endAnchor().y() - item->pagePos().y();
     392              : 
     393         8755 :     createBeamSegments(item, ctx, chordRests);
     394         8755 :     setTremAnchors(item, ctx);
     395         8778 : }
     396              : 
     397       269395 : bool BeamLayout::isStartOfNonCrossBeam(ChordRest* cr)
     398              : {
     399       269395 :     Beam* b = cr->beam();
     400       269395 :     if (b && b->elements().front() == cr) {
     401        12821 :         if (b->cross()) {
     402           30 :             return false;
     403              :         }
     404              : 
     405        12791 :         return true;
     406              :     }
     407              : 
     408       256574 :     return false;
     409              : }
     410              : 
     411        92140 : bool BeamLayout::isStartOfCrossBeam(ChordRest* cr)
     412              : {
     413        92140 :     Beam* b = cr->beam();
     414        92140 :     if (b && b->elements().front() == cr) {
     415         4225 :         if (b->cross()) {
     416           10 :             return true;
     417              :         }
     418              :     }
     419              : 
     420        92130 :     return false;
     421              : }
     422              : 
     423              : //---------------------------------------------------------
     424              : //   restoreBeams
     425              : //---------------------------------------------------------
     426              : 
     427          142 : void BeamLayout::restoreBeams(Measure* m, LayoutContext& ctx)
     428              : {
     429          381 :     for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
     430         1455 :         for (EngravingItem* e : s->elist()) {
     431         1216 :             if (e && e->isChordRest()) {
     432          316 :                 ChordRest* cr = toChordRest(e);
     433          316 :                 Beam* b = cr->beam();
     434          316 :                 if (b && !b->elements().empty() && b->elements().front() == cr) {
     435            0 :                     TLayout::layoutBeam(b, ctx);
     436            0 :                     b->addSkyline(m->system()->staff(b->staffIdx())->skyline());
     437              :                 }
     438              :             }
     439              :         }
     440              :     }
     441          142 : }
     442              : 
     443          178 : bool BeamLayout::measureMayHaveBeamsJoinedIntoNext(const Measure* measure)
     444              : {
     445          178 :     const MeasureBase* next = measure->next();
     446          178 :     if (!(next && next->isMeasure())) {
     447            9 :         return false;
     448              :     }
     449              : 
     450          169 :     const Measure* nextMeasure = toMeasure(next);
     451          169 :     const Segment* firstCrSeg = nextMeasure->findFirstR(SegmentType::ChordRest, Fraction(0, 1));
     452          169 :     if (!firstCrSeg) {
     453            0 :         return false;
     454              :     }
     455              : 
     456         1165 :     for (const EngravingItem* item : firstCrSeg->elist()) {
     457          996 :         if (!item) {
     458          742 :             continue;
     459              :         }
     460          254 :         BeamMode beamMode = toChordRest(item)->beamMode();
     461          254 :         if (beamMode == BeamMode::MID || beamMode == BeamMode::BEGIN16 || beamMode == BeamMode::BEGIN32) {
     462            0 :             return true;
     463              :         }
     464              :     }
     465              : 
     466          169 :     return false;
     467              : }
     468              : 
     469              : //---------------------------------------------------------
     470              : //   breakCrossMeasureBeams
     471              : //---------------------------------------------------------
     472              : 
     473         7150 : void BeamLayout::breakCrossMeasureBeams(Measure* measure, LayoutContext& ctx)
     474              : {
     475         7150 :     MeasureBase* mbNext = measure->next();
     476         7150 :     if (!mbNext || !mbNext->isMeasure()) {
     477         3858 :         return;
     478              :     }
     479              : 
     480         3292 :     Measure* next = toMeasure(mbNext);
     481         3292 :     const size_t ntracks = ctx.dom().ntracks();
     482         3292 :     Segment* fstSeg = next->first(SegmentType::ChordRest);
     483         3292 :     if (!fstSeg) {
     484            0 :         return;
     485              :     }
     486              : 
     487        22248 :     for (size_t track = 0; track < ntracks; ++track) {
     488        18956 :         const Staff* stf = ctx.dom().staff(track2staff(track));
     489              : 
     490              :         // don’t compute beams for invisible staves and tablature without stems
     491        18956 :         if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->stemless())) {
     492        18954 :             continue;
     493              :         }
     494              : 
     495        18544 :         EngravingItem* e = fstSeg->element(static_cast<int>(track));
     496        18544 :         if (!e || !e->isChordRest()) {
     497        13744 :             continue;
     498              :         }
     499              : 
     500         4800 :         ChordRest* cr = toChordRest(e);
     501         4800 :         Beam* beam = cr->beam();
     502         4800 :         if (!beam || beam->elements().front()->measure() == next) {   // no beam or not cross-measure beam
     503         4798 :             continue;
     504              :         }
     505              : 
     506            2 :         std::vector<ChordRest*> mElements;
     507            2 :         std::vector<ChordRest*> nextElements;
     508              : 
     509           16 :         for (ChordRest* beamCR : beam->elements()) {
     510           14 :             if (beamCR->tick() < next->tick()) {
     511            6 :                 mElements.push_back(beamCR);
     512              :             } else {
     513            8 :                 nextElements.push_back(beamCR);
     514              :             }
     515              :         }
     516              : 
     517            2 :         if (mElements.size() == 1) {
     518            0 :             mElements[0]->removeDeleteBeam(false);
     519              :         }
     520              : 
     521            2 :         Beam* newBeam = nullptr;
     522            2 :         if (nextElements.size() > 1) {
     523            2 :             newBeam = Factory::createBeam(ctx.mutDom().score());
     524            2 :             newBeam->setGenerated(true);
     525            2 :             newBeam->setTrack(track);
     526              :         }
     527              : 
     528            2 :         const bool nextBeamed = bool(newBeam);
     529           10 :         for (ChordRest* nextCR : nextElements) {
     530            8 :             nextCR->removeDeleteBeam(nextBeamed);
     531            8 :             if (newBeam) {
     532            8 :                 newBeam->add(nextCR);
     533              :             }
     534              :         }
     535              : 
     536            2 :         if (newBeam) {
     537            2 :             layout1(newBeam, ctx);
     538              :         }
     539            2 :     }
     540              : }
     541              : 
     542           26 : static bool beamNoContinue(BeamMode mode)
     543              : {
     544           26 :     return mode == BeamMode::END || mode == BeamMode::NONE || mode == BeamMode::INVALID;
     545              : }
     546              : 
     547              : #define beamModeMid(a) (a == BeamMode::MID || a == BeamMode::BEGIN16 || a == BeamMode::BEGIN32)
     548              : 
     549              : //---------------------------------------------------------
     550              : //   beamGraceNotes
     551              : //---------------------------------------------------------
     552              : 
     553        95100 : void BeamLayout::beamGraceNotes(LayoutContext& ctx, Chord* mainNote, bool after)
     554              : {
     555        95100 :     ChordRest* a1    = 0;        // start of (potential) beam
     556        95100 :     Beam* beam       = 0;        // current beam
     557        95100 :     BeamMode bm = BeamMode::AUTO;
     558        95100 :     std::vector<Chord*> graceNotes = after ? mainNote->graceNotesAfter() : mainNote->graceNotesBefore();
     559              : 
     560        95100 :     if (beam) {
     561            0 :         beam->setIsGrace(true);
     562              :     }
     563              : 
     564        95980 :     for (ChordRest* cr : graceNotes) {
     565          880 :         bm = Groups::baseBeamMode(cr);
     566          880 :         if ((cr->durationType().type() <= DurationType::V_QUARTER) || (bm == BeamMode::NONE)) {
     567          277 :             if (beam) {
     568            5 :                 beam->setIsGrace(true);
     569            5 :                 layout1(beam, ctx);
     570            5 :                 beam = 0;
     571              :             }
     572          277 :             if (a1) {
     573            1 :                 a1->removeDeleteBeam(false);
     574            1 :                 a1 = 0;
     575              :             }
     576          277 :             cr->removeDeleteBeam(false);
     577          277 :             continue;
     578              :         }
     579          603 :         if (beam) {
     580           72 :             bool beamEnd = bm == BeamMode::BEGIN;
     581           72 :             if (!beamEnd) {
     582           72 :                 cr->replaceBeam(beam);
     583           72 :                 cr = 0;
     584           72 :                 beamEnd = (bm == BeamMode::END);
     585              :             }
     586           72 :             if (beamEnd) {
     587           28 :                 beam->setIsGrace(true);
     588           28 :                 layout1(beam, ctx);
     589           28 :                 beam = 0;
     590              :             }
     591              :         }
     592          603 :         if (!cr) {
     593           72 :             continue;
     594              :         }
     595          531 :         if (a1 == 0) {
     596          439 :             a1 = cr;
     597              :         } else {
     598           92 :             if (!beamModeMid(bm) && (bm == BeamMode::BEGIN)) {
     599            0 :                 a1->removeDeleteBeam(false);
     600            0 :                 a1 = cr;
     601              :             } else {
     602           92 :                 beam = a1->beam();
     603           92 :                 if (beam == 0 || beam->elements().front() != a1) {
     604           33 :                     beam = Factory::createBeam(ctx.mutDom().score());
     605           33 :                     beam->setGenerated(true);
     606           33 :                     beam->setTrack(mainNote->track());
     607           33 :                     a1->replaceBeam(beam);
     608              :                 }
     609           92 :                 cr->replaceBeam(beam);
     610           92 :                 a1 = 0;
     611              :             }
     612              :         }
     613              :     }
     614        95100 :     if (beam) {
     615           59 :         beam->setIsGrace(true);
     616           59 :         layout1(beam, ctx);
     617        95041 :     } else if (a1) {
     618          346 :         a1->removeDeleteBeam(false);
     619              :     }
     620        95100 : }
     621              : 
     622        30347 : void BeamLayout::createBeams(LayoutContext& ctx, Measure* measure)
     623              : {
     624        30362 :     TRACEFUNC;
     625              : 
     626       207651 :     for (track_idx_t track = 0; track < ctx.dom().ntracks(); ++track) {
     627       177304 :         const Staff* stf = ctx.dom().staff(track2staff(track));
     628              : 
     629              :         // don’t compute beams for invisible staves and tablature without stems
     630       177304 :         if (!stf->show() || (stf->isTabStaff(measure->tick()) && stf->staffType(measure->tick())->stemless())) {
     631         6028 :             continue;
     632              :         }
     633              : 
     634       171276 :         ChordRest* a1    = nullptr;          // start of (potential) beam
     635       171276 :         bool firstCR     = true;
     636       171276 :         Beam* beam       = nullptr;          // current beam
     637       171276 :         BeamMode bm    = BeamMode::AUTO;
     638       171276 :         ChordRest* prev  = nullptr;
     639       171276 :         std::unordered_map<int, TDuration> beatSubdivision;
     640              : 
     641              :         // if this measure is simple meter (actually X/4),
     642              :         // then perform a prepass to determine the subdivision of each beat
     643              : 
     644       171276 :         beatSubdivision.clear();
     645       171276 :         TimeSig* ts = stf->timeSig(measure->tick());
     646       171276 :         const SegmentType st = SegmentType::ChordRest;
     647       171276 :         if (ts && ts->denominator() == 4) {
     648       164516 :             Fraction stretch = ts->stretch();
     649       494444 :             for (Segment* s = measure->first(st); s; s = s->next(st)) {
     650       329928 :                 ChordRest* mcr = toChordRest(s->element(track));
     651       329928 :                 if (mcr == 0) {
     652       247930 :                     continue;
     653              :                 }
     654        81998 :                 int beat = (mcr->rtick() * stretch).ticks() / Constants::DIVISION;
     655        81998 :                 if (muse::contains(beatSubdivision, beat)) {
     656        11733 :                     beatSubdivision[beat] = std::min(beatSubdivision[beat], mcr->durationType());
     657              :                 } else {
     658        70265 :                     beatSubdivision[beat] = mcr->durationType();
     659              :                 }
     660              :             }
     661              :         }
     662              : 
     663       517788 :         for (Segment* segment = measure->first(st); segment; segment = segment->next(st)) {
     664       346512 :             ChordRest* cr = segment->cr(track);
     665       346512 :             if (!cr) {
     666       260675 :                 continue;
     667              :             }
     668              : 
     669        85837 :             if (cr->isChord()) {
     670        47550 :                 Chord* chord = toChord(cr);
     671        48430 :                 for (Chord* c : chord->graceNotes()) {
     672          880 :                     c->setBeamlet(nullptr); // Will be defined during beam layout
     673              :                 }
     674              :             }
     675        85837 :             cr->setBeamlet(nullptr); // Will be defined during beam layout
     676              : 
     677        85837 :             if (firstCR) {
     678        44193 :                 firstCR = false;
     679              :                 // Handle cross-measure beams
     680        44193 :                 BeamMode mode = cr->beamMode();
     681        44193 :                 if (mode == BeamMode::MID || mode == BeamMode::END || mode == BeamMode::BEGIN16 || mode == BeamMode::BEGIN32) {
     682           26 :                     ChordRest* prevCR = ctx.mutDom().findCR(measure->tick() - Fraction::eps(), track);
     683           26 :                     if (prevCR) {
     684           26 :                         Beam* prevBeam = prevCR->beam();
     685           26 :                         const Measure* pm = prevCR->measure();
     686           26 :                         if (!beamNoContinue(prevCR->beamMode())
     687           26 :                             && !pm->lineBreak() && !pm->pageBreak() && !pm->sectionBreak()
     688           26 :                             && ctx.state().prevMeasure()
     689           52 :                             && !(prevCR->isChord() && prevCR->durationType().type() <= DurationType::V_QUARTER)) {
     690           26 :                             beam = prevBeam;
     691           26 :                             if (prevCR->isChord()) {
     692           22 :                                 if (Hook* hook = toChord(prevCR)->hook()) {
     693            0 :                                     ctx.mutDom().doUndoRemoveElement(hook);
     694            0 :                                     prevCR->segment()->staffShape(prevCR->vStaffIdx()).remove_if([hook](const ShapeElement& el) {
     695            0 :                                         return el.item() == hook;
     696              :                                     });
     697              :                                 }
     698              :                             }
     699              :                             //a1 = beam ? beam->elements().front() : prevCR;
     700           26 :                             a1 = beam ? nullptr : prevCR;               // when beam is found, a1 is no longer required.
     701            0 :                         } else if (prevBeam && prevBeam == cr->beam() && prevBeam->elements().front() == prevCR) {
     702              :                             // remove the beam from the previous chordrest because we do not currently
     703              :                             // support cross-system beams
     704            0 :                             prevCR->removeDeleteBeam(false);
     705              :                         }
     706              :                     }
     707              :                 }
     708              :             }
     709              : 
     710        85837 :             bool hasAllRest = cr->beam() && cr->beam()->hasAllRests();
     711        85837 :             bool isARestBeamStart = cr->isRest() && cr->beamMode() == BeamMode::BEGIN;
     712        85837 :             if (hasAllRest && !isARestBeamStart) {
     713            0 :                 cr->removeDeleteBeam(false);
     714              :             }
     715              : 
     716              :             // handle grace notes
     717              :             // (tied chords?)
     718        85837 :             if (cr->isChord()) {
     719        47550 :                 Chord* chord = toChord(cr);
     720        47550 :                 beamGraceNotes(ctx, chord, false);         // grace before
     721        47550 :                 beamGraceNotes(ctx, chord, true);          // grace after
     722              :             }
     723              : 
     724        85837 :             bm = Groups::actualBeamMode(cr, prev, &beatSubdivision);
     725              : 
     726        85837 :             prev = cr;
     727              : 
     728              :             // Rests of any duration can be beamed over, if required
     729        85837 :             bool canBeBeamed = cr->durationType().type() > DurationType::V_QUARTER || cr->isRest();
     730        85837 :             if (!canBeBeamed || (bm == BeamMode::NONE)) {
     731        69893 :                 bool removeBeam = true;
     732        69893 :                 if (beam) {
     733         1270 :                     layout1(beam, ctx);
     734         1270 :                     removeBeam = (beam->elements().size() <= 1 || beam->hasAllRests());
     735         1270 :                     beam = 0;
     736              :                 }
     737        69893 :                 if (a1) {
     738         1198 :                     if (removeBeam) {
     739         1198 :                         a1->removeDeleteBeam(false);
     740              :                     }
     741         1198 :                     a1 = 0;
     742              :                 }
     743        69893 :                 cr->removeDeleteBeam(false);
     744        69893 :                 continue;
     745        69893 :             }
     746              : 
     747        15944 :             if (beam) {
     748         7400 :                 bool beamEnd = (bm == BeamMode::BEGIN);
     749         7400 :                 if (!beamEnd) {
     750         5963 :                     cr->replaceBeam(beam);
     751         5963 :                     cr = 0;
     752         5963 :                     beamEnd = (bm == BeamMode::END);
     753              :                 }
     754         7400 :                 if (beamEnd) {
     755         1573 :                     layout1(beam, ctx);
     756         1573 :                     beam = 0;
     757              :                 }
     758              :             }
     759        15944 :             if (!cr) {
     760         5963 :                 continue;
     761              :             }
     762              : 
     763         9981 :             if (a1 == 0) {
     764         5652 :                 a1 = cr;
     765              :             } else {
     766          169 :                 if (!beamModeMid(bm)
     767         4530 :                     &&
     768              :                     (bm == BeamMode::BEGIN
     769           32 :                      || (a1->segment()->segmentType() != cr->segment()->segmentType())
     770         4361 :                      || (a1->endTick() < cr->tick())
     771              :                     )
     772              :                     ) {
     773          126 :                     a1->removeDeleteBeam(false);
     774          126 :                     a1 = cr;
     775              :                 } else {
     776         4203 :                     beam = a1->beam();
     777         4203 :                     if (beam == 0 || beam->elements().front() != a1) {
     778         1095 :                         beam = Factory::createBeam(ctx.mutDom().score());
     779         1095 :                         beam->setGenerated(true);
     780         1095 :                         beam->setTrack(track);
     781         1095 :                         a1->replaceBeam(beam);
     782              :                     }
     783         4203 :                     cr->replaceBeam(beam);
     784         4203 :                     a1 = 0;
     785              :                 }
     786              :             }
     787              :         }
     788       171276 :         if (beam) {
     789         1386 :             layout1(beam, ctx);
     790       169890 :         } else if (a1) {
     791          251 :             Fraction nextTick = a1->endTick();
     792          251 :             Measure* m = (nextTick >= measure->endTick() ? measure->nextMeasure() : measure);
     793          251 :             ChordRest* nextCR = (m ? m->findChordRest(nextTick, track) : nullptr);
     794          251 :             Beam* b = a1->beam();
     795          263 :             if (!(b && !b->elements().empty() && !b->hasAllRests() && b->elements().front() == a1 && nextCR
     796           12 :                   && beamModeMid(nextCR->beamMode()))) {
     797          247 :                 a1->removeDeleteBeam(false);
     798              :             }
     799              :         }
     800       171276 :     }
     801        30347 : }
     802              : 
     803              : /************************************************************
     804              :  * layoutNonCrossBeams()
     805              :  * layout all non-cross-staff beams starting on this segment
     806              :  * **********************************************************/
     807              : 
     808       178594 : void BeamLayout::layoutNonCrossBeams(ChordRest* cr, LayoutContext& ctx)
     809              : {
     810       178594 :     if (cr->isChord()) {
     811        99955 :         for (Chord* grace : toChord(cr)->graceNotes()) {
     812         1810 :             layoutNonCrossBeams(grace, ctx);
     813              :         }
     814              :     }
     815              : 
     816       178594 :     if (!BeamLayout::isStartOfNonCrossBeam(cr)) {
     817       170014 :         return;
     818              :     }
     819              : 
     820         8580 :     Beam* beam = cr->beam();
     821              : 
     822         8580 :     TLayout::layoutBeam(beam, ctx);
     823              : 
     824         8580 :     if (!beam->tremAnchors().empty()) {
     825              :         // there are inset tremolos in here
     826          120 :         for (ChordRest* beamCr : beam->elements()) {
     827           80 :             if (!beamCr->isChord()) {
     828            0 :                 continue;
     829              :             }
     830           80 :             Chord* c = toChord(beamCr);
     831           80 :             if (c->tremoloTwoChord()) {
     832           80 :                 TremoloLayout::layout(c->tremoloTwoChord(), ctx);
     833              :             }
     834              :         }
     835              :     }
     836              : 
     837        37752 :     for (ChordRest* beamCR : beam->elements()) {
     838        29172 :         if (beamCR->isRest() && beamCR->vStaffIdx() == beam->staffIdx()) {
     839           58 :             verticalAdjustBeamedRests(toRest(beamCR), beam, ctx);
     840              :         }
     841              :     }
     842              : }
     843              : 
     844           58 : void BeamLayout::verticalAdjustBeamedRests(Rest* rest, Beam* beam, LayoutContext& ctx)
     845              : {
     846           58 :     const double spatium = rest->spatium();
     847              :     static constexpr Fraction rest32nd(1, 32);
     848           58 :     const bool up = beam->up();
     849           58 :     const BeamMode restBeamMode = rest->beamMode();
     850           58 :     const bool firstRest = beam->elements().front() == rest
     851           58 :                            && (restBeamMode == BeamMode::BEGIN || restBeamMode == BeamMode::BEGIN16 || restBeamMode == BeamMode::BEGIN32
     852            0 :                                || restBeamMode == BeamMode::MID);
     853              : 
     854              :     double restToBeamPadding;
     855           58 :     if (rest->ticks() <= rest32nd) {
     856            2 :         restToBeamPadding = 0.2 * spatium;
     857              :     } else {
     858           56 :         restToBeamPadding = 0.35 * spatium;
     859              :     }
     860              : 
     861           58 :     const Shape beamShape = beam->shape().translate(beam->pagePos());
     862           58 :     const Shape restShape = rest->shape().translate(rest->pagePos() - rest->offset());
     863           58 :     const double minBeamToRestXDist = up && firstRest ? 0.1 * spatium : 0.0;
     864              : 
     865              :     const double restToBeamClearance = up
     866           58 :                                        ? beamShape.verticalClearance(restShape, minBeamToRestXDist)
     867           32 :                                        : restShape.verticalClearance(beamShape);
     868           58 :     const double lineDistance = rest->staff()->lineDistance(rest->tick()) * spatium;
     869              : 
     870           58 :     int clearanceInSteps = std::ceil(restToBeamClearance / lineDistance);
     871           58 :     up ? rest->verticalClearance().setAbove(clearanceInSteps) : rest->verticalClearance().setBelow(clearanceInSteps);
     872              : 
     873           58 :     if (restToBeamClearance > restToBeamPadding) {
     874           49 :         return;
     875              :     }
     876              : 
     877            9 :     const bool restIsLocked = rest->verticalClearance().locked();
     878            9 :     if (!restIsLocked) {
     879            9 :         double overlap = (restToBeamPadding - restToBeamClearance);
     880            9 :         int lineMoves = ceil(overlap / lineDistance);
     881            9 :         lineMoves *= up ? 1 : -1;
     882            9 :         double yMove = lineMoves * lineDistance;
     883            9 :         rest->mutldata()->moveY(yMove);
     884            9 :         for (Rest* mergedRest : rest->ldata()->mergedRests) {
     885            0 :             mergedRest->mutldata()->moveY(yMove);
     886              :         }
     887              : 
     888            9 :         Segment* segment = rest->segment();
     889            9 :         staff_idx_t staffIdx = rest->vStaffIdx();
     890            9 :         const Staff* staff = ctx.dom().staff(staffIdx);
     891            9 :         std::vector<Chord*> chords;
     892            9 :         std::vector<Rest*> rests;
     893            9 :         collectChordsAndRest(segment, staffIdx, chords, rests);
     894            9 :         RestLayout::resolveRestVSChord(rests, chords, staff, segment);
     895            9 :         RestLayout::resolveRestVSRest(rests, staff, segment, ctx, /*considerBeams*/ true);
     896            9 :     }
     897              : 
     898            9 :     TLayout::layoutBeam(beam, ctx);
     899              : 
     900            9 :     rest->segment()->createShape(rest->vStaffIdx());
     901          107 : }
     902              : 
     903          102 : void BeamLayout::checkCrossPosAndStemConsistency(Beam* beam, LayoutContext& ctx)
     904              : {
     905          102 :     if (!beam->cross() || !beam->userModified()) {
     906          102 :         return;
     907              :     }
     908              : 
     909            0 :     int correctCrossStaffIdx = 0;
     910            0 :     bool inconsistencyFound = false;
     911              : 
     912            0 :     for (ChordRest* cr : beam->elements()) {
     913            0 :         if (!cr->isChord()) {
     914            0 :             continue;
     915              :         }
     916            0 :         Chord* chord = toChord(cr);
     917            0 :         bool currentUp = chord->up();
     918            0 :         bool actualUp = ChordLayout::isChordPosBelowBeam(chord, beam);
     919            0 :         if (actualUp != currentUp) {
     920            0 :             inconsistencyFound = true;
     921            0 :             chord->setUp(actualUp);
     922            0 :             ChordLayout::layoutChords1(ctx, chord->segment(), chord->staffIdx());
     923            0 :             chord->segment()->createShape(chord->staffIdx());
     924              :         }
     925            0 :         if (actualUp) {
     926            0 :             correctCrossStaffIdx = std::min(correctCrossStaffIdx, chord->staffMove());
     927              :         } else {
     928            0 :             correctCrossStaffIdx = std::max(correctCrossStaffIdx, chord->staffMove() + 1);
     929              :         }
     930              :     }
     931              : 
     932            0 :     int beamCrossStaffIdx = beam->crossStaffIdx();
     933            0 :     if (beamCrossStaffIdx != correctCrossStaffIdx) {
     934            0 :         inconsistencyFound = true;
     935            0 :         int curCrossStaffMove = beam->crossStaffMove();
     936            0 :         int diff = correctCrossStaffIdx - beamCrossStaffIdx;
     937            0 :         beam->setProperty(Pid::BEAM_CROSS_STAFF_MOVE, curCrossStaffMove + diff);
     938              :     }
     939              : 
     940            0 :     if (inconsistencyFound) {
     941            0 :         BeamLayout::layout(beam, ctx);
     942              :     }
     943              : }
     944              : 
     945         8778 : void BeamLayout::createBeamSegments(Beam* item, const LayoutContext& ctx, const std::vector<ChordRest*>& chordRests)
     946              : {
     947         8778 :     item->clearBeamSegments();
     948              : 
     949         8778 :     bool levelHasBeam = false;
     950         8778 :     int level = 0;
     951         8778 :     constexpr size_t noLastChord = std::numeric_limits<size_t>::max();
     952         8778 :     size_t numCr = chordRests.size();
     953         8778 :     bool frenchStyleBeams = ctx.conf().styleB(Sid::frenchStyleBeams);
     954        20362 :     do {
     955        20362 :         levelHasBeam = false;
     956        20362 :         ChordRest* startCr = nullptr;
     957        20362 :         ChordRest* endCr = nullptr;
     958        20362 :         bool breakBeam = false;
     959        20362 :         bool previousBreak16 = false;
     960        20362 :         bool previousBreak32 = false;
     961        20362 :         size_t lastChordIndex = noLastChord;
     962              : 
     963        94159 :         for (size_t i = 0; i < numCr; i++) {
     964        73797 :             ChordRest* chordRest = chordRests[i];
     965        73797 :             ChordRest* prevChordRest = i < 1 ? nullptr : chordRests[i - 1];
     966              : 
     967        73797 :             if (level < chordRest->beams() && !chordRest->isRest()) {
     968        42009 :                 levelHasBeam = true;
     969              :             }
     970        73797 :             bool isBroken16 = false;
     971        73797 :             bool isBroken32 = false;
     972              : 
     973              :             // updates isBroken16 and isBroken32
     974        73797 :             item->calcBeamBreaks(chordRest, prevChordRest, level, isBroken16, isBroken32);
     975        73797 :             breakBeam = isBroken16 || isBroken32;
     976        73797 :             bool skipRest = false;
     977        73797 :             BeamMode beamMode = chordRest->beamMode();
     978        74044 :             if (chordRest->isRest() && level >= chordRest->beams()
     979        74044 :                 && (beamMode == BeamMode::MID || beamMode == BeamMode::BEGIN16 || beamMode == BeamMode::BEGIN32)) {
     980              :                 // if the level is lower than both the previous and next real chords, don't break here
     981          123 :                 size_t nextChordIndex = i + 1;
     982          155 :                 while (nextChordIndex < numCr && chordRests[nextChordIndex]->isRest()) {
     983           32 :                     ++nextChordIndex;
     984              :                 }
     985          123 :                 if (lastChordIndex < numCr && nextChordIndex < numCr) {
     986          123 :                     Chord* lastChord = toChord(chordRests[lastChordIndex]);
     987          123 :                     Chord* nextChord = toChord(chordRests[nextChordIndex]);
     988              :                     // we have both chords (not rests) on either side of this rest, so if the level is lower than both we can
     989              :                     // just continue the beam through here
     990              :                     // this creates situations where we can have the second beam of a pair of 16th notes continuing through
     991              :                     // an 8th rest, for example, even though an 8th note would very obviously break the beam
     992              :                     // this is desired for BeamType::MID on rests.
     993          123 :                     if (level < lastChord->beams() && level < nextChord->beams()) {
     994            0 :                         skipRest = (isBroken16 && level < 1) || (isBroken32 && level < 2) || (!isBroken16 && !isBroken32);
     995              :                     }
     996              :                 }
     997              :             }
     998        73797 :             if ((level < chordRest->beams() && !breakBeam) || skipRest) {
     999        40980 :                 endCr = chordRest;
    1000        40980 :                 if (!startCr || (startCr->isRest() && startCr != item->elements().front())) {
    1001        11188 :                     startCr = chordRest;
    1002              :                 }
    1003        32817 :             } else if (level >= chordRest->beams() && chordRest->isRest() && !breakBeam) {
    1004              :                 // This rest has duration longer than the beam value, but it may still go
    1005              :                 // under the beam if the next chords or rests are beamed
    1006          123 :                 continue;
    1007              :             } else {
    1008        32694 :                 size_t beamletIndex = static_cast<size_t>(i) - 1;
    1009        32694 :                 if (lastChordIndex < item->elements().size() && (chordRest->isRest() || (endCr && endCr->isRest()))) {
    1010              :                     // we broke the beam on this chordrest, but the last cr of the beam segment can't end on a rest
    1011              :                     // so it ends on lastChord
    1012            6 :                     ChordRest* lastCr = toChordRest(item->elements()[lastChordIndex]);
    1013            6 :                     if (lastCr && startCr && lastCr->tick() >= startCr->tick()) {
    1014            6 :                         endCr = lastCr;
    1015            6 :                         beamletIndex = lastChordIndex;
    1016            6 :                         lastChordIndex = noLastChord;
    1017              :                     }
    1018              :                 }
    1019        32694 :                 if (startCr && endCr && levelHasBeam) {
    1020         1112 :                     if (startCr == endCr && startCr->isChord()) {
    1021          214 :                         bool isBeamletBefore = calcIsBeamletBefore(item,
    1022              :                                                                    toChord(startCr),
    1023              :                                                                    static_cast<int>(beamletIndex),
    1024              :                                                                    level,
    1025              :                                                                    previousBreak16,
    1026              :                                                                    previousBreak32);
    1027          214 :                         createBeamletSegment(item, ctx, toChord(startCr), isBeamletBefore, level);
    1028          898 :                     } else if (startCr != endCr) {
    1029          898 :                         createBeamSegment(item, startCr, endCr, level, frenchStyleBeams);
    1030              :                     }
    1031              :                 }
    1032        32694 :                 bool setCr = chordRest && chordRest->isChord() && breakBeam && level < chordRest->beams();
    1033        32694 :                 startCr = setCr ? chordRest : nullptr;
    1034        32694 :                 endCr = setCr ? chordRest : nullptr;
    1035              :             }
    1036        73674 :             previousBreak16 = isBroken16;
    1037        73674 :             previousBreak32 = isBroken32;
    1038        73674 :             if (chordRest && chordRest->isChord()) {
    1039        73550 :                 lastChordIndex = i;
    1040              :             }
    1041              :         }
    1042              : 
    1043              :         // if the beam ends on the last chord
    1044        20362 :         if (startCr && (endCr || breakBeam)) {
    1045        11229 :             if ((startCr == endCr || !endCr) && startCr->isChord()) {
    1046              :                 // this chord is either the last chord, or the first (followed by a rest)
    1047          183 :                 bool isBefore = !(startCr == chordRests.front());
    1048          183 :                 createBeamletSegment(item, ctx, toChord(startCr), isBefore, level);
    1049              :             } else {
    1050        11046 :                 createBeamSegment(item, startCr, endCr, level, frenchStyleBeams);
    1051              :             }
    1052              :         }
    1053        20362 :         level++;
    1054              :     } while (levelHasBeam);
    1055         8778 : }
    1056              : 
    1057          214 : bool BeamLayout::calcIsBeamletBefore(const Beam* item, Chord* chord, int i, int level, bool isAfter16Break, bool isAfter32Break)
    1058              : {
    1059              :     // if first or last chord in beam group
    1060          214 :     if (i == 0) {
    1061          142 :         return false;
    1062           72 :     } else if (i == static_cast<int>(item->elements().size()) - 1) {
    1063            0 :         return true;
    1064              :     }
    1065              :     // if first or last chord in tuplet
    1066           72 :     Tuplet* tuplet = chord->tuplet();
    1067           72 :     if (tuplet && chord == tuplet->elements().front()) {
    1068           10 :         return false;
    1069           62 :     } else if (tuplet && chord == tuplet->elements().back()) {
    1070           14 :         return true;
    1071              :     }
    1072              : 
    1073              :     // next note has a beam break
    1074           48 :     ChordRest* nextChordRest = item->elements()[i + 1];
    1075           48 :     ChordRest* currChordRest = item->elements()[i];
    1076           48 :     ChordRest* prevChordRest = item->elements()[i - 1];
    1077           48 :     if (nextChordRest->isChord()) {
    1078           48 :         bool nextBreak16 = false;
    1079           48 :         bool nextBreak32 = false;
    1080           48 :         bool currBreak16 = false;
    1081           48 :         bool currBreak32 = false;
    1082           48 :         item->calcBeamBreaks(currChordRest, prevChordRest, prevChordRest->beams(), currBreak16, currBreak32);
    1083           48 :         item->calcBeamBreaks(nextChordRest, currChordRest, level, nextBreak16, nextBreak32);
    1084           48 :         if ((nextBreak16 && level >= 1) || (!currBreak16 && nextBreak32 && level >= 2)) {
    1085           26 :             return true;
    1086              :         }
    1087              :     }
    1088              : 
    1089              :     // if previous or next chord has more beams, point in that direction
    1090           22 :     int previousChordLevel = -1;
    1091           22 :     int nextChordLevel = -1;
    1092           22 :     int previousOffset = 1;
    1093           22 :     while (i - previousOffset >= 0) {
    1094           22 :         ChordRest* previous = item->elements()[i - previousOffset];
    1095           22 :         if (previous->isChord()) {
    1096           22 :             previousChordLevel = toChord(previous)->beams();
    1097           22 :             if (isAfter16Break) {
    1098           10 :                 previousChordLevel = std::min(previousChordLevel, 1);
    1099           12 :             } else if (isAfter32Break) {
    1100            0 :                 previousChordLevel = std::min(previousChordLevel, 2);
    1101              :             }
    1102           22 :             break;
    1103              :         }
    1104            0 :         ++previousOffset;
    1105              :     }
    1106              : 
    1107           22 :     int nextOffset = 1;
    1108           22 :     while (i + nextOffset < static_cast<int>(item->elements().size())) {
    1109           22 :         ChordRest* next = item->elements()[i + nextOffset];
    1110           22 :         if (next->isChord()) {
    1111           22 :             nextChordLevel = toChord(next)->beams();
    1112           22 :             break;
    1113              :         }
    1114            0 :         ++nextOffset;
    1115              :     }
    1116           22 :     int chordLevelDifference = nextChordLevel - previousChordLevel;
    1117           22 :     if (chordLevelDifference != 0) {
    1118           14 :         return chordLevelDifference < 0;
    1119              :     }
    1120              : 
    1121              :     // if the chord ends a subdivision of the beat
    1122            8 :     Fraction baseTick = tuplet ? tuplet->tick() : chord->measure()->tick();
    1123            8 :     Fraction stretch = chord->staff()->timeStretch(baseTick);
    1124            8 :     Fraction tickNext = (nextChordRest->tick() - baseTick) * stretch;
    1125            8 :     if (tuplet) {
    1126              :         // for tuplets with odd ratios, apply ratio
    1127              :         // for tuplets with even ratios, use actual beat
    1128            0 :         Fraction ratio = tuplet->ratio();
    1129            0 :         if (ratio.numerator() & 1) {
    1130            0 :             tickNext *= ratio;
    1131              :         }
    1132              :     }
    1133              : 
    1134              :     static const int BEAM_TUPLET_TOLERANCE = 6;
    1135            8 :     int tickLargeSize  = chord->ticks().ticks() * 2;
    1136            8 :     int remainder = tickNext.ticks() % tickLargeSize;
    1137            8 :     if (remainder <= BEAM_TUPLET_TOLERANCE || (tickLargeSize - remainder) <= BEAM_TUPLET_TOLERANCE) {
    1138            4 :         return true;
    1139              :     }
    1140              : 
    1141              :     // default case
    1142            4 :     return false;
    1143              : }
    1144              : 
    1145        11825 : static inline int computeStemLengthAdjustmentSteps(const ChordRest* cr, bool isStartOrEnd, bool isAbove, bool frenchStyleBeams,
    1146              :                                                    int level, int beamsAbove, int beamsBelow)
    1147              : {
    1148        11825 :     if (isAbove == cr->up()) { // beam segment on opposite side
    1149           34 :         if (!frenchStyleBeams || isStartOrEnd) {
    1150              :             // lengthen
    1151           34 :             return level - (isAbove ? beamsBelow : beamsAbove);
    1152              :         }
    1153              :     } else { // beamlet on the note side
    1154        11791 :         if (frenchStyleBeams && !isStartOrEnd) {
    1155              :             // shorten
    1156            0 :             return (isAbove ? beamsBelow : beamsAbove) - level;
    1157              :         }
    1158              :     }
    1159              : 
    1160        11791 :     return 0;
    1161              : }
    1162              : 
    1163        11944 : void BeamLayout::createBeamSegment(Beam* item, ChordRest* startCr, ChordRest* endCr, int level, bool frenchStyleBeams)
    1164              : {
    1165        11944 :     const bool isFirstSubgroup = startCr == item->elements().front();
    1166        11944 :     const bool isLastSubgroup = endCr == item->elements().back();
    1167        11944 :     const bool firstUp = startCr->up();
    1168        11944 :     const bool lastUp = endCr->up();
    1169        11944 :     bool overallUp = item->up();
    1170        11944 :     if (isFirstSubgroup == isLastSubgroup) {
    1171              :         // this subgroup is either the only one in the beam, or in the middle
    1172        10825 :         if (firstUp == lastUp) {
    1173              :             // the "outside notes" of this subgroup go the same direction so use them
    1174              :             // to determine the side of the beams
    1175        10805 :             overallUp = firstUp;
    1176              :         } else {
    1177              :             // no perfect way to solve this problem, for now we'll base it on the number of
    1178              :             // up and down stemmed notes in this subgroup
    1179              :             int upStems, downStems;
    1180           20 :             upStems = downStems = 0;
    1181           62 :             for (ChordRest* cr : item->elements()) {
    1182           62 :                 if (!cr->isChord() || cr->tick() < startCr->tick()) {
    1183            0 :                     continue;
    1184              :                 }
    1185           62 :                 if (cr->tick() > endCr->tick()) {
    1186            0 :                     break;
    1187              :                 }
    1188              : 
    1189           62 :                 ++(toChord(cr)->up() ? upStems : downStems);
    1190              : 
    1191           62 :                 if (cr == endCr) {
    1192           20 :                     break;
    1193              :                 }
    1194              :             }
    1195           20 :             if (upStems == downStems) {
    1196              :                 // we are officially bamboozled. for now we can just use the default
    1197              :                 // direction based on the staff we're on I guess
    1198           14 :                 overallUp = item->up();
    1199              :             } else {
    1200              :                 // use the direction with the most stems
    1201            6 :                 overallUp = upStems > downStems;
    1202              :             }
    1203              :         }
    1204         1119 :     } else if (isFirstSubgroup) {
    1205          418 :         overallUp = lastUp;
    1206          701 :     } else if (isLastSubgroup) {
    1207          701 :         overallUp = firstUp;
    1208              :     }
    1209              : 
    1210        11944 :     const double startX = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), startCr, ChordBeamAnchorType::Start);
    1211        11944 :     const double endX = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), endCr, ChordBeamAnchorType::End);
    1212              : 
    1213        11944 :     double startY = item->slope() * (startX - item->startAnchor().x()) + item->startAnchor().y() - item->pagePos().y();
    1214        11944 :     double endY = item->slope() * (endX - item->startAnchor().x()) + item->startAnchor().y() - item->pagePos().y();
    1215              : 
    1216        11944 :     int beamsBelow = 0; // how many beams below level 0?
    1217        11944 :     int beamsAbove = 0; // how many beams above level 0?
    1218              : 
    1219              :     // avoid adjusting for beams on opposite side of level 0
    1220        11944 :     if (level != 0) {
    1221         9682 :         for (const BeamSegment* beam : item->beamSegments()) {
    1222         6516 :             if (beam->level == 0 || beam->endTick < startCr->tick() || beam->startTick > endCr->tick()) {
    1223         5301 :                 continue;
    1224              :             }
    1225         1215 :             ++(beam->above ? beamsAbove : beamsBelow);
    1226              :         }
    1227              : 
    1228         3166 :         const int upValue = overallUp ? -1 : 1;
    1229         3166 :         const int extraBeamAdjust = overallUp ? beamsAbove : beamsBelow;
    1230         3166 :         const double verticalOffset = item->beamDist() * (level - extraBeamAdjust) * upValue;
    1231              : 
    1232         3166 :         if (muse::RealIsEqual(item->growLeft(), item->growRight())) {
    1233         3150 :             startY -= verticalOffset * item->growLeft();
    1234         3150 :             endY -= verticalOffset * item->growLeft();
    1235              :         } else {
    1236              :             // Feathered beams
    1237           16 :             double startProportionAlongX = (startX - item->startAnchor().x()) / (item->endAnchor().x() - item->startAnchor().x());
    1238           16 :             double endProportionAlongX = (endX - item->startAnchor().x()) / (item->endAnchor().x() - item->startAnchor().x());
    1239              : 
    1240           16 :             double grow1 = startProportionAlongX * (item->growRight() - item->growLeft()) + item->growLeft();
    1241           16 :             double grow2 = endProportionAlongX * (item->growRight() - item->growLeft()) + item->growLeft();
    1242              : 
    1243           16 :             startY -= verticalOffset * grow1;
    1244           16 :             endY -= verticalOffset * grow2;
    1245              :         }
    1246              :     }
    1247              : 
    1248        11944 :     BeamSegment* b = new BeamSegment(item);
    1249        11944 :     b->above = !overallUp;
    1250        11944 :     b->level = level;
    1251        11944 :     b->line = LineF(startX, startY, endX, endY);
    1252        11944 :     b->startTick = startCr->tick();
    1253        11944 :     b->endTick = endCr->tick();
    1254        11944 :     item->beamSegments().push_back(b);
    1255              : 
    1256        11944 :     if (level > 0) {
    1257         3166 :         ++(b->above ? beamsAbove : beamsBelow);
    1258              :     }
    1259              : 
    1260              :     // extend stems properly
    1261        45802 :     for (ChordRest* cr : item->elements()) {
    1262        45779 :         if (!cr->isChord() || cr->tick() < startCr->tick()) {
    1263         4100 :             continue;
    1264              :         }
    1265        41679 :         if (cr->tick() > endCr->tick()) {
    1266            4 :             break;
    1267              :         }
    1268              : 
    1269        41675 :         Chord* chord = toChord(cr);
    1270        41675 :         if (!chord->stem()) {
    1271           94 :             continue;
    1272              :         }
    1273              : 
    1274        41581 :         double _up = chord->up() ? -1.0 : 1.0;
    1275              : 
    1276        41581 :         if (level == 0) {
    1277        29756 :             BeamTremoloLayout::extendStem(item->ldata(), chord, 0.0);
    1278              :             // HACK: extend the bounding box of the stem (NOT the stem itself) by half beam width.
    1279              :             // This way the bbox of the stem covers also the beam position. Hugely helps with all the collision checks.
    1280        29756 :             chord->stem()->mutldata()->beamCorrection = _up * item->beamWidth() / 2;
    1281              :         } else {
    1282        11825 :             bool isStartOrEnd = cr == startCr || cr == endCr;
    1283        11825 :             int visualStemLengthAdjustmentSteps = computeStemLengthAdjustmentSteps(cr, isStartOrEnd, b->above, frenchStyleBeams,
    1284              :                                                                                    level, beamsAbove, beamsBelow);
    1285        11825 :             if (visualStemLengthAdjustmentSteps != 0) {
    1286           34 :                 double grow = item->growLeft();
    1287           34 :                 if (!muse::RealIsEqual(item->growLeft(), item->growRight())) {
    1288            0 :                     double anchorX = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), chord, ChordBeamAnchorType::Middle);
    1289            0 :                     double proportionAlongX = (anchorX - item->startAnchor().x()) / (item->endAnchor().x() - item->startAnchor().x());
    1290            0 :                     grow = proportionAlongX * (item->growRight() - item->growLeft()) + item->growLeft();
    1291              :                 }
    1292              : 
    1293           34 :                 double visualStemLengthAdjustment = grow * visualStemLengthAdjustmentSteps * item->beamDist();
    1294              : 
    1295           34 :                 if (frenchStyleBeams) {
    1296              :                     // Even if we visually shorten the stem, we need the 'original' length for collisions.
    1297            0 :                     int bboxStemLengthAdjustmentSteps = computeStemLengthAdjustmentSteps(cr, isStartOrEnd, b->above,
    1298              :                                                                                          /*frenchStyleBeams*/ false,
    1299              :                                                                                          level, beamsAbove, beamsBelow);
    1300            0 :                     chord->stem()->mutldata()->beamCorrection = _up * item->beamWidth() / 2 + _up * grow
    1301            0 :                                                                 * (bboxStemLengthAdjustmentSteps - visualStemLengthAdjustmentSteps)
    1302            0 :                                                                 * item->beamDist();
    1303              :                 }
    1304           34 :                 BeamTremoloLayout::extendStem(item->ldata(), chord, visualStemLengthAdjustment);
    1305              :             }
    1306              :         }
    1307              : 
    1308        41581 :         if (cr == endCr) {
    1309        11917 :             break;
    1310              :         }
    1311              :     }
    1312        11944 : }
    1313              : 
    1314          397 : void BeamLayout::createBeamletSegment(Beam* item, const LayoutContext& ctx, ChordRest* cr, bool isBefore, int level)
    1315              : {
    1316          397 :     const double startX = BeamTremoloLayout::chordBeamAnchorX(item->ldata(), cr,
    1317              :                                                               isBefore
    1318              :                                                               ? ChordBeamAnchorType::End
    1319              :                                                               : ChordBeamAnchorType::Start);
    1320              : 
    1321          397 :     const double beamletLength = ctx.conf().styleAbsolute(Sid::beamMinLen) * cr->mag();
    1322              : 
    1323          397 :     const double endX = startX + (isBefore ? -beamletLength : beamletLength);
    1324              : 
    1325          397 :     double startY = item->slope() * (startX - item->startAnchor().x()) + item->startAnchor().y() - item->pagePos().y();
    1326          397 :     double endY = item->slope() * (endX - startX) + startY;
    1327              : 
    1328              :     // how many beams past level 0 (i.e. beams on the other side of level 0 for this subgroup)
    1329          397 :     int extraBeamAdjust = 0;
    1330              : 
    1331              :     // avoid adjusting for beams on opposite side of level 0
    1332         1332 :     for (const BeamSegment* beam : item->beamSegments()) {
    1333          935 :         if (beam->level == 0 || beam->line.x2() < startX || beam->line.x1() > endX) {
    1334          802 :             continue;
    1335              :         }
    1336              : 
    1337          133 :         if (cr->up() == beam->above) {
    1338            0 :             extraBeamAdjust++;
    1339              :         }
    1340              :     }
    1341              : 
    1342          397 :     const int upValue = cr->up() ? -1 : 1;
    1343          397 :     const double verticalOffset = item->beamDist() * (level - extraBeamAdjust) * upValue;
    1344              : 
    1345          397 :     if (muse::RealIsEqual(item->growLeft(), item->growRight())) {
    1346          397 :         startY -= verticalOffset * item->growLeft();
    1347          397 :         endY -= verticalOffset * item->growLeft();
    1348              :     } else {
    1349              :         // Feathered beams
    1350            0 :         double startProportionAlongX = (startX - item->startAnchor().x()) / (item->endAnchor().x() - item->startAnchor().x());
    1351            0 :         double endProportionAlongX = (endX - item->startAnchor().x()) / (item->endAnchor().x() - item->startAnchor().x());
    1352              : 
    1353            0 :         double grow1 = startProportionAlongX * (item->growRight() - item->growLeft()) + item->growLeft();
    1354            0 :         double grow2 = endProportionAlongX * (item->growRight() - item->growLeft()) + item->growLeft();
    1355              : 
    1356            0 :         startY -= verticalOffset * grow1;
    1357            0 :         endY -= verticalOffset * grow2;
    1358              :     }
    1359              : 
    1360          397 :     BeamSegment* b = new BeamSegment(item);
    1361          397 :     b->above = !cr->up();
    1362          397 :     b->level = level;
    1363          397 :     b->line = LineF(startX, startY, endX, endY);
    1364          397 :     b->isBeamlet = true;
    1365          397 :     b->isBefore = isBefore;
    1366          397 :     cr->setBeamlet(b);
    1367              : 
    1368          397 :     ChordLayout::fillShape(cr, cr->mutldata(), ctx.conf());
    1369              :     //! NOTE Moved from ChordRest::setBeamlet
    1370          397 :     cr->segment()->createShape(cr->vStaffIdx());
    1371          397 :     item->beamSegments().push_back(b);
    1372          397 : }
    1373              : 
    1374         4346 : bool BeamLayout::computeUpForMovedCross(Beam* item)
    1375              : {
    1376         4346 :     if (!item->cross()) {
    1377         4336 :         return false;
    1378              :     }
    1379           10 :     bool isAboveAllChords = true;
    1380           10 :     bool isBelowAllChords = true;
    1381           45 :     for (ChordRest* cr : item->elements()) {
    1382           35 :         if (cr->isBelowCrossBeam(item)) {
    1383           17 :             isBelowAllChords = false;
    1384              :         } else {
    1385           18 :             isAboveAllChords = false;
    1386              :         }
    1387              :     }
    1388              : 
    1389           10 :     if (isAboveAllChords) {
    1390            0 :         item->setUp(true);
    1391            0 :         return true;
    1392              :     }
    1393              : 
    1394           10 :     if (isBelowAllChords) {
    1395            0 :         item->setUp(false);
    1396            0 :         return true;
    1397              :     }
    1398              : 
    1399           10 :     return false;
    1400              : }
    1401              : 
    1402          103 : PointF BeamLayout::chordBeamAnchor(const Beam* item, const ChordRest* chord, ChordBeamAnchorType anchorType)
    1403              : {
    1404          103 :     return BeamTremoloLayout::chordBeamAnchor(item->ldata(), chord, anchorType);
    1405              : }
    1406              : 
    1407            0 : double BeamLayout::chordBeamAnchorY(const Beam* item, const ChordRest* chord)
    1408              : {
    1409            0 :     return BeamTremoloLayout::chordBeamAnchorY(item->ldata(), chord);
    1410              : }
    1411              : 
    1412         8778 : void BeamLayout::setTremAnchors(Beam* item, const LayoutContext& ctx)
    1413              : {
    1414         8778 :     item->tremAnchors().clear();
    1415        38651 :     for (ChordRest* cr : item->elements()) {
    1416        29873 :         if (!cr || !cr->isChord()) {
    1417           75 :             continue;
    1418              :         }
    1419        29798 :         Chord* c = toChord(cr);
    1420        29798 :         TremoloTwoChord* t = c ? c->tremoloTwoChord() : nullptr;
    1421        29798 :         if (t && t->chord1() == c && t->chord2()->beam() == item) {
    1422              :             // there is an inset tremolo here!
    1423              :             // figure out up / down
    1424           40 :             bool tremUp = t->up();
    1425           40 :             if (item->userModified()) {
    1426            0 :                 tremUp = c->up();
    1427           40 :             } else if (item->cross() && t->chord1()->staffMove() == t->chord2()->staffMove()) {
    1428            0 :                 tremUp = t->chord1()->staffMove() == item->maxCRMove();
    1429              :             }
    1430           40 :             TremAnchor tremAnchor;
    1431           40 :             tremAnchor.chord1 = c;
    1432           40 :             int regularBeams = c->beams(); // non-tremolo strokes
    1433           40 :             double scale = t->spatium() * t->mag();
    1434              : 
    1435              :             // find the left-side anchor
    1436           40 :             double width = item->endAnchor().x() - item->startAnchor().x();
    1437           40 :             double height = item->endAnchor().y() - item->startAnchor().y();
    1438           40 :             double x = chordBeamAnchor(item, c, ChordBeamAnchorType::Middle).x();
    1439           40 :             double proportionAlongX = (x - item->startAnchor().x()) / width;
    1440           40 :             double y = item->startAnchor().y() + (proportionAlongX * height);
    1441           40 :             y += regularBeams * (ctx.conf().styleB(Sid::useWideBeams) ? 1.0 : 0.75) * scale * (tremUp ? 1. : -1.);
    1442           40 :             tremAnchor.y1 = y;
    1443              :             // find the right-side anchor
    1444           40 :             x = chordBeamAnchor(item, t->chord2(), ChordBeamAnchorType::Middle).x();
    1445           40 :             proportionAlongX = (x - item->startAnchor().x()) / width;
    1446           40 :             y = item->startAnchor().y() + (proportionAlongX * height);
    1447           40 :             y += regularBeams * (ctx.conf().styleB(Sid::useWideBeams) ? 1.0 : 0.75) * scale * (tremUp ? 1. : -1.);
    1448           40 :             tremAnchor.y2 = y;
    1449           40 :             item->tremAnchors().push_back(tremAnchor);
    1450              :         }
    1451              :     }
    1452         8778 : }
        

Generated by: LCOV version 2.0-1