LCOV - code coverage report
Current view: top level - src/engraving/dom - beam.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 55.6 % 423 235
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) 2021 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              : 
      23              : #include "beam.h"
      24              : 
      25              : #include <cmath>
      26              : #include <algorithm>
      27              : 
      28              : #include "containers.h"
      29              : #include "realfn.h"
      30              : 
      31              : #include "../editing/elementeditdata.h"
      32              : 
      33              : #include "actionicon.h"
      34              : #include "chord.h"
      35              : #include "groups.h"
      36              : #include "measure.h"
      37              : #include "rootitem.h"
      38              : #include "score.h"
      39              : #include "segment.h"
      40              : #include "staff.h"
      41              : #include "system.h"
      42              : #include "tremolotwochord.h"
      43              : #include "tuplet.h"
      44              : 
      45              : #include "log.h"
      46              : 
      47              : using namespace mu;
      48              : using namespace muse;
      49              : using namespace mu::engraving;
      50              : 
      51              : namespace mu::engraving {
      52              : static const ElementStyle beamStyle {
      53              :     { Sid::beamNoSlope,                        Pid::BEAM_NO_SLOPE },
      54              : };
      55              : 
      56              : //---------------------------------------------------------
      57              : //   Beam
      58              : //---------------------------------------------------------
      59              : 
      60         2872 : Beam::Beam(Score* parent)
      61         2872 :     : BeamBase(ElementType::BEAM, parent->dummy())
      62              : {
      63         2872 :     initElementStyle(&beamStyle);
      64         2872 :     resetProperty(Pid::BEAM_CROSS_STAFF_MOVE);
      65         2872 : }
      66              : 
      67       648031 : System* Beam::system() const
      68              : {
      69       648031 :     if (m_elements.empty()) {
      70           58 :         return nullptr;
      71              :     }
      72       647973 :     Measure* measure = m_elements.front()->measure();
      73       647973 :     return measure ? measure->system() : nullptr;
      74              : }
      75              : 
      76       639253 : EngravingItem* Beam::layoutParent() const
      77              : {
      78       639253 :     return system();
      79              : }
      80              : 
      81            4 : EngravingItem* Beam::accessibleParentItem() const
      82              : {
      83              :     // The system a beam is drawn on does not own it and cannot list it, so in the
      84              :     // accessibility tree the beam stays where it is parked: on the dummy.
      85            4 :     return score()->dummy()->rootItem();
      86              : }
      87              : 
      88              : //---------------------------------------------------------
      89              : //   Beam
      90              : //---------------------------------------------------------
      91              : 
      92           18 : Beam::Beam(const Beam& b)
      93           18 :     : BeamBase(b)
      94              : {
      95           18 :     m_elements     = b.m_elements;
      96           18 :     m_growLeft           = b.m_growLeft;
      97           18 :     m_growRight           = b.m_growRight;
      98           18 :     m_beamDist        = b.m_beamDist;
      99           18 :     m_fragments.reserve(b.m_fragments.size());
     100           36 :     for (const BeamFragment* f : b.m_fragments) {
     101           18 :         m_fragments.push_back(new BeamFragment(*f));
     102              :     }
     103           18 :     m_minCRMove          = b.m_minCRMove;
     104           18 :     m_maxCRMove          = b.m_maxCRMove;
     105           18 :     m_isGrace          = b.m_isGrace;
     106           18 :     m_cross            = b.m_cross;
     107           18 :     m_fullCross        = b.m_fullCross;
     108           18 :     m_slope            = b.m_slope;
     109           18 : }
     110              : 
     111              : //---------------------------------------------------------
     112              : //   Beam
     113              : //---------------------------------------------------------
     114              : 
     115         4904 : Beam::~Beam()
     116              : {
     117              :     //
     118              :     // delete all references from chords
     119              :     //
     120         2452 :     for (ChordRest* cr : m_elements) {
     121            0 :         cr->setBeam(nullptr);
     122              :     }
     123              : 
     124         2452 :     clearBeamSegments();
     125              : 
     126         2452 :     muse::DeleteAll(m_fragments);
     127         2452 :     m_fragments.clear();
     128         4904 : }
     129              : 
     130              : //---------------------------------------------------------
     131              : //   add
     132              : //---------------------------------------------------------
     133              : 
     134         9619 : void Beam::add(EngravingItem* e)
     135              : {
     136         9619 :     if (e->isChordRest()) {
     137         9619 :         addChordRest(toChordRest(e));
     138         9619 :         e->added();
     139              :     }
     140         9619 : }
     141              : 
     142              : //---------------------------------------------------------
     143              : //   remove
     144              : //---------------------------------------------------------
     145              : 
     146         1541 : void Beam::remove(EngravingItem* e)
     147              : {
     148         1541 :     if (e->isChordRest()) {
     149         1541 :         removeChordRest(toChordRest(e));
     150         1541 :         e->removed();
     151              :     }
     152         1541 : }
     153              : 
     154              : //---------------------------------------------------------
     155              : //   addChordRest
     156              : //---------------------------------------------------------
     157              : 
     158         9619 : void Beam::addChordRest(ChordRest* a)
     159              : {
     160         9619 :     a->setBeam(this);
     161         9619 :     if (!muse::contains(m_elements, a)) {
     162              :         //
     163              :         // insert element in same order as it appears
     164              :         // in the score
     165              :         //
     166         9619 :         if (a->segment() && !m_elements.empty()) {
     167        23282 :             for (size_t i = 0; i < m_elements.size(); ++i) {
     168        16626 :                 Segment* s = m_elements[i]->segment();
     169        16626 :                 if ((s->tick() > a->segment()->tick())
     170        16626 :                     || ((s->tick() == a->segment()->tick()) && (a->segment()->next(SegmentType::ChordRest) == s))
     171              :                     ) {
     172            1 :                     m_elements.insert(m_elements.begin() + i, a);
     173            1 :                     return;
     174              :                 }
     175              :             }
     176              :         }
     177         9618 :         m_elements.push_back(a);
     178              :     }
     179              : }
     180              : 
     181              : //---------------------------------------------------------
     182              : //   removeChordRest
     183              : //---------------------------------------------------------
     184              : 
     185         1541 : void Beam::removeChordRest(ChordRest* a)
     186              : {
     187         1541 :     if (!muse::remove(m_elements, a)) {
     188            0 :         LOGD("Beam::remove(): cannot find ChordRest");
     189              :     }
     190         1541 :     a->setBeam(0);
     191         1541 : }
     192              : 
     193         2664 : const Chord* Beam::findChordWithCustomStemDirection() const
     194              : {
     195        11941 :     for (const ChordRest* rest: elements()) {
     196         9293 :         if (!rest->isChord()) {
     197           29 :             continue;
     198              :         }
     199              : 
     200         9264 :         const Chord* chord = toChord(rest);
     201         9264 :         if (chord->stemDirection() != DirectionV::AUTO) {
     202           16 :             return chord;
     203              :         }
     204              :     }
     205              : 
     206         2648 :     return nullptr;
     207              : }
     208              : 
     209           64 : const BeamSegment* Beam::topLevelSegmentForElement(const ChordRest* element) const
     210              : {
     211           64 :     const std::vector<BeamSegment*>& segments = beamSegments();
     212           64 :     size_t segmentsSize = segments.size();
     213              : 
     214           64 :     IF_ASSERT_FAILED(segmentsSize > 0) {
     215            0 :         return nullptr;
     216              :     }
     217              : 
     218           64 :     const BeamSegment* curSegment = segments[0];
     219           64 :     if (segmentsSize == 1) {
     220           16 :         return curSegment;
     221              :     }
     222              : 
     223          144 :     for (const BeamSegment* segment : segments) {
     224           96 :         if (segment->level <= curSegment->level) {
     225           48 :             continue;
     226              :         }
     227           48 :         Fraction elementTick = element->tick();
     228           48 :         if (segment->startTick <= elementTick && segment->endTick >= elementTick) {
     229           48 :             curSegment = segment;
     230              :         }
     231              :     }
     232              : 
     233           48 :     return curSegment;
     234              : }
     235              : 
     236              : //---------------------------------------------------------
     237              : //   move
     238              : //---------------------------------------------------------
     239              : 
     240            0 : void Beam::move(const PointF& offset)
     241              : {
     242            0 :     EngravingItem::move(offset);
     243            0 :     for (BeamSegment* bs : m_beamSegments) {
     244            0 :         bs->line.translate(offset);
     245              :     }
     246            0 : }
     247              : 
     248        74294 : void Beam::calcBeamBreaks(const ChordRest* cr, const ChordRest* prevCr, int level, bool& isBroken16, bool& isBroken32) const
     249              : {
     250        74294 :     BeamMode beamMode = cr->beamMode();
     251        74294 :     if (cr->isRest() && (beamMode == BeamMode::MID || beamMode == BeamMode::BEGIN16 || beamMode == BeamMode::BEGIN32)) {
     252              :         // when a rest has beamMode MID we can just ignore it entirely and allow any beams to continue through
     253          251 :         switch (beamMode) {
     254          251 :         case BeamMode::MID:
     255          251 :             isBroken16 = isBroken32 = false;
     256          251 :             break;
     257            0 :         case BeamMode::BEGIN16:
     258            0 :             isBroken16 = level > 0;
     259            0 :             isBroken32 = false;
     260            0 :             break;
     261            0 :         case BeamMode::BEGIN32:
     262            0 :             isBroken16 = false;
     263            0 :             isBroken32 = level > 1;
     264            0 :             break;
     265            0 :         default:
     266              :             // should be unreachable
     267            0 :             assert(false);
     268              :         }
     269          251 :         return;
     270              :     }
     271              :     // get default beam mode -- based on time signature preferences
     272        74043 :     const Groups& group = cr->staff()->group(cr->measure()->tick());
     273        74043 :     BeamMode defaultBeamMode = group.baseBeamMode(cr, prevCr);
     274              : 
     275        74043 :     bool isManuallyBroken16 = level >= 1 && beamMode == BeamMode::BEGIN16;
     276        74043 :     bool isManuallyBroken32 = level >= 2 && beamMode == BeamMode::BEGIN32;
     277        74043 :     bool isDefaultBroken16 = beamMode == BeamMode::AUTO && level >= 1 && defaultBeamMode == BeamMode::BEGIN16;
     278        74043 :     bool isDefaultBroken32 = beamMode == BeamMode::AUTO && level >= 2 && defaultBeamMode == BeamMode::BEGIN32;
     279              : 
     280        74043 :     isBroken16 = isManuallyBroken16 || isDefaultBroken16;
     281        74043 :     isBroken32 = isManuallyBroken32 || isDefaultBroken32;
     282              : 
     283              :     // deal with beam-embedded triplets by breaking beams as if they are their underlying durations
     284              :     // note that we use max(hooks, 1) here because otherwise we'd end up breaking the main (level 0) beam for
     285              :     // tuplets that take up non-beamed amounts of space (eg. 16th note quintuplets)
     286              :     // a tuplet's total duration needn't be a single note value, so truncate it
     287        74043 :     if (level > 0 && prevCr && cr->beamMode() == BeamMode::AUTO) {
     288        25053 :         if (cr->tuplet() && cr->tuplet() != prevCr->tuplet()) {
     289              :             // this cr starts a tuplet
     290         1580 :             int beams = std::max(TDuration(cr->tuplet()->ticks(), /*truncate*/ true).hooks(), 1);
     291         1580 :             if (beams <= level) {
     292         1430 :                 isBroken16 = level == 1;
     293         1430 :                 isBroken32 = level >= 2;
     294              :             }
     295        23473 :         } else if (prevCr->tuplet() && prevCr->tuplet() != cr->tuplet()) {
     296              :             // this is a non-tuplet cr that is first after a tuplet
     297           45 :             int beams = std::max(TDuration(prevCr->tuplet()->ticks(), /*truncate*/ true).hooks(), 1);
     298           45 :             if (beams <= level) {
     299           45 :                 isBroken16 = level == 1;
     300           45 :                 isBroken32 = level >= 2;
     301              :             }
     302              :         }
     303              :     }
     304              : }
     305              : 
     306              : //---------------------------------------------------------
     307              : //   spatiumChanged
     308              : //---------------------------------------------------------
     309              : 
     310            2 : void Beam::spatiumChanged(double oldValue, double newValue)
     311              : {
     312            2 :     int idx = directionIdx();
     313            2 :     if (userModified()) {
     314            0 :         double diff = newValue / oldValue;
     315            0 :         for (BeamFragment* f : m_fragments) {
     316            0 :             f->py1[idx] = f->py1[idx] * diff;
     317            0 :             f->py2[idx] = f->py2[idx] * diff;
     318              :         }
     319              :     }
     320            2 : }
     321              : 
     322              : //---------------------------------------------------------
     323              : //   BeamEditData
     324              : //---------------------------------------------------------
     325              : 
     326              : class BeamEditData : public ElementEditData
     327              : {
     328            0 :     OBJECT_ALLOCATOR(engraving, BeamEditData)
     329              : public:
     330              :     int editFragment;
     331            0 :     virtual EditDataType type() override { return EditDataType::BeamEditData; }
     332              : };
     333              : 
     334              : //---------------------------------------------------------
     335              : //   dragGrip
     336              : //---------------------------------------------------------
     337              : 
     338            0 : void Beam::dragGrip(EditData& ed)
     339              : {
     340            0 :     int idx = directionIdx();
     341            0 :     double dy = ed.delta.y();
     342            0 :     BeamEditData* bed = static_cast<BeamEditData*>(ed.getData(this).get());
     343            0 :     BeamFragment* f = m_fragments[bed->editFragment];
     344            0 :     double y1 = f->py1[idx];
     345            0 :     double y2 = f->py2[idx];
     346              : 
     347            0 :     if (ed.curGrip == Grip::MIDDLE || noSlope()) {
     348            0 :         y1 += dy;
     349            0 :         y2 += dy;
     350            0 :     } else if (ed.curGrip == Grip::START) {
     351            0 :         y1 += dy;
     352            0 :     } else if (ed.curGrip == Grip::END) {
     353            0 :         y2 += dy;
     354              :     } else {
     355            0 :         UNREACHABLE;
     356              :         return;
     357              :     }
     358              : 
     359            0 :     double _spatium = spatium();
     360              :     // Because of the logic in Beam::setProperty(),
     361              :     // changing Pid::BEAM_POS only has an effect if Pid::USER_MODIFIED is true.
     362            0 :     undoChangeProperty(Pid::USER_MODIFIED, true);
     363            0 :     undoChangeProperty(Pid::BEAM_POS, PairF(y1 / _spatium, y2 / _spatium));
     364            0 :     undoChangeProperty(Pid::GENERATED, false);
     365              : 
     366            0 :     triggerLayout();
     367              : }
     368              : 
     369              : //---------------------------------------------------------
     370              : //   gripsPositions
     371              : //---------------------------------------------------------
     372              : 
     373            0 : std::vector<PointF> Beam::gripsPositions(const EditData& ed) const
     374              : {
     375            0 :     int idx = directionIdx();
     376            0 :     BeamEditData* bed = static_cast<BeamEditData*>(ed.getData(this).get());
     377            0 :     BeamFragment* f = m_fragments[bed->editFragment];
     378              : 
     379            0 :     ChordRest* c1 = nullptr;
     380            0 :     ChordRest* c2 = nullptr;
     381            0 :     size_t n = m_elements.size();
     382              : 
     383            0 :     if (n == 0) {
     384            0 :         return std::vector<PointF>();
     385              :     }
     386              : 
     387            0 :     for (size_t i = 0; i < n; ++i) {
     388            0 :         if (m_elements[i]->isChordRest()) {
     389            0 :             c1 = toChordRest(m_elements[i]);
     390            0 :             break;
     391              :         }
     392              :     }
     393            0 :     if (!c1) { // no chord/rest found, no need to check again below
     394            0 :         return {}; // just ignore the requested operation
     395              :     }
     396            0 :     for (int i = static_cast<int>(n) - 1; i >= 0; --i) {
     397            0 :         if (m_elements[i]->isChordRest()) {
     398            0 :             c2 = toChordRest(m_elements[i]);
     399            0 :             break;
     400              :         }
     401              :     }
     402            0 :     if (!c2) { // no chord/rest found, no need to check again below
     403            0 :         return {}; // just ignore the requested operation
     404              :     }
     405              : 
     406            0 :     int y = pagePos().y();
     407            0 :     double beamStartX = m_startAnchor.x() + (system() ? system()->x() : 0);
     408            0 :     double beamEndX = m_endAnchor.x() + (system() ? system()->x() : 0);
     409            0 :     double middleX = (beamStartX + beamEndX) / 2;
     410            0 :     double middleY = (f->py1[idx] + y + f->py2[idx] + y) / 2;
     411              : 
     412              :     return {
     413            0 :         PointF(beamStartX, f->py1[idx] + y),
     414            0 :         PointF(beamEndX, f->py2[idx] + y),
     415              :         PointF(middleX, middleY)
     416            0 :     };
     417              : }
     418              : 
     419              : //---------------------------------------------------------
     420              : //   setBeamDirection
     421              : //---------------------------------------------------------
     422              : 
     423          783 : void Beam::setDirection(DirectionV d)
     424              : {
     425          783 :     if (direction() == d || m_cross) {
     426           95 :         return;
     427              :     }
     428              : 
     429          688 :     doSetDirection(d);
     430              : 
     431          688 :     if (d != DirectionV::AUTO) {
     432          687 :         setUp(d == DirectionV::UP);
     433              :     }
     434              : 
     435          700 :     for (ChordRest* e : elements()) {
     436           12 :         if (e->isChord()) {
     437           12 :             toChord(e)->undoChangeProperty(Pid::STEM_DIRECTION, d);
     438              :         }
     439              :     }
     440              : }
     441              : 
     442            2 : void Beam::setAsFeathered(const bool slower)
     443              : {
     444            2 :     if (slower) {
     445            1 :         undoChangeProperty(Pid::GROW_LEFT, 1.0);
     446            1 :         undoChangeProperty(Pid::GROW_RIGHT, 0.0);
     447              :     } else {
     448            1 :         undoChangeProperty(Pid::GROW_LEFT, 0.0);
     449            1 :         undoChangeProperty(Pid::GROW_RIGHT, 1.0);
     450              :     }
     451            2 : }
     452              : 
     453              : //---------------------------------------------------------
     454              : //   reset
     455              : //---------------------------------------------------------
     456              : 
     457            0 : void Beam::reset()
     458              : {
     459            0 :     if (!RealIsEqual(growLeft(), 1.0)) {
     460            0 :         undoChangeProperty(Pid::GROW_LEFT, 1.0);
     461              :     }
     462            0 :     if (!RealIsEqual(growRight(), 1.0)) {
     463            0 :         undoChangeProperty(Pid::GROW_RIGHT, 1.0);
     464              :     }
     465            0 :     if (userModified()) {
     466            0 :         undoChangeProperty(Pid::BEAM_POS, PropertyValue::fromValue(beamPos()));
     467            0 :         undoChangeProperty(Pid::USER_MODIFIED, false);
     468              :     }
     469            0 :     undoChangeProperty(Pid::STEM_DIRECTION, DirectionV::AUTO);
     470            0 :     undoResetProperty(Pid::BEAM_NO_SLOPE);
     471            0 :     undoResetProperty(Pid::BEAM_CROSS_STAFF_MOVE);
     472            0 :     undoChangeProperty(Pid::GENERATED, true);
     473            0 : }
     474              : 
     475              : //---------------------------------------------------------
     476              : //   startEdit
     477              : //---------------------------------------------------------
     478              : 
     479            0 : void Beam::startEdit(EditData& ed)
     480              : {
     481            0 :     initBeamEditData(ed);
     482            0 : }
     483              : 
     484              : //---------------------------------------------------------
     485              : //   triggerLayout
     486              : //---------------------------------------------------------
     487              : 
     488         9522 : void Beam::triggerLayout() const
     489              : {
     490         9522 :     if (!m_elements.empty()) {
     491           69 :         m_elements.front()->triggerLayout();
     492           69 :         m_elements.back()->triggerLayout();
     493              :     }
     494         9522 : }
     495              : 
     496              : //---------------------------------------------------------
     497              : //   acceptDrop
     498              : //---------------------------------------------------------
     499              : 
     500            0 : bool Beam::acceptDrop(EditData& data) const
     501              : {
     502            0 :     EngravingItem* e = data.dropElement;
     503              : 
     504            0 :     if (e->isActionIcon()) {
     505            0 :         ActionIconType type = toActionIcon(e)->actionType();
     506              :         return type == ActionIconType::BEAM_FEATHERED_DECELERATE
     507            0 :                || type == ActionIconType::BEAM_FEATHERED_ACCELERATE;
     508              :     }
     509              : 
     510            0 :     return false;
     511              : }
     512              : 
     513              : //---------------------------------------------------------
     514              : //   drop
     515              : //---------------------------------------------------------
     516              : 
     517            0 : EngravingItem* Beam::drop(Transaction&, EditData& data)
     518              : {
     519            0 :     if (!data.dropElement->isActionIcon()) {
     520            0 :         return nullptr;
     521              :     }
     522              : 
     523            0 :     ActionIcon* e = toActionIcon(data.dropElement);
     524              : 
     525            0 :     if (e->actionType() == ActionIconType::BEAM_FEATHERED_DECELERATE) {
     526            0 :         setAsFeathered(true /*slower*/);
     527            0 :     } else if (e->actionType() == ActionIconType::BEAM_FEATHERED_ACCELERATE) {
     528            0 :         setAsFeathered(false /*slower*/);
     529              :     }
     530              : 
     531            0 :     return nullptr;
     532              : }
     533              : 
     534              : //---------------------------------------------------------
     535              : //   beamPos
     536              : //---------------------------------------------------------
     537              : 
     538            0 : PairF Beam::beamPos() const
     539              : {
     540            0 :     if (m_fragments.empty()) {
     541            0 :         return PairF(0.0, 0.0);
     542              :     }
     543            0 :     BeamFragment* f = m_fragments.back();
     544            0 :     int idx = directionIdx();
     545            0 :     double _spatium = spatium();
     546            0 :     return PairF(f->py1[idx] / _spatium, f->py2[idx] / _spatium);
     547              : }
     548              : 
     549              : //---------------------------------------------------------
     550              : //   setBeamPos
     551              : //---------------------------------------------------------
     552              : 
     553            0 : void Beam::setBeamPos(const PairF& bp)
     554              : {
     555            0 :     if (m_fragments.empty()) {
     556            0 :         m_fragments.push_back(new BeamFragment);
     557              :     }
     558            0 :     BeamFragment* f = m_fragments.back();
     559            0 :     int idx = directionIdx();
     560            0 :     setUserModified(true);
     561            0 :     setGenerated(false);
     562              : 
     563            0 :     double _spatium = spatium();
     564            0 :     if (noSlope()) {
     565            0 :         f->py1[idx] = f->py2[idx] = (bp.first + bp.second) * 0.5 * _spatium;
     566              :     } else {
     567            0 :         f->py1[idx] = bp.first * _spatium;
     568            0 :         f->py2[idx] = bp.second * _spatium;
     569              :     }
     570            0 : }
     571              : 
     572         2877 : void Beam::setNoSlope(bool b)
     573              : {
     574         2877 :     m_noSlope = b;
     575              : 
     576              :     // Make flat if usermodified
     577         2877 :     if (m_noSlope) {
     578            9 :         int idx = directionIdx();
     579            9 :         if (userModified()) {
     580            0 :             BeamFragment* f = m_fragments.back();
     581            0 :             f->py1[idx] = f->py2[idx] = (f->py1[idx] + f->py2[idx]) * 0.5;
     582              :         }
     583              :     }
     584         2877 : }
     585              : 
     586         8778 : void Beam::computeAndSetSlope()
     587              : {
     588         8778 :     double xDiff = m_endAnchor.x() - m_startAnchor.x();
     589         8778 :     double yDiff = m_endAnchor.y() - m_startAnchor.y();
     590         8778 :     if (std::abs(xDiff) < 0.5 * spatium()) {
     591              :         // Temporary safeguard: a beam this short is invalid, and exists only as a temporary state,
     592              :         // so don't try to compute the slope as it will be wrong. Needs a better solution in future.
     593         2809 :         setSlope(0.0);
     594              :     } else {
     595         5969 :         setSlope(yDiff / xDiff);
     596              :     }
     597         8778 : }
     598              : 
     599              : //---------------------------------------------------------
     600              : //   getProperty
     601              : //---------------------------------------------------------
     602              : 
     603        21257 : PropertyValue Beam::getProperty(Pid propertyId) const
     604              : {
     605        21257 :     switch (propertyId) {
     606         1624 :     case Pid::GROW_LEFT:      return growLeft();
     607         1624 :     case Pid::GROW_RIGHT:     return growRight();
     608            0 :     case Pid::BEAM_POS:       return PropertyValue::fromValue(beamPos());
     609            0 :     case Pid::BEAM_NO_SLOPE:  return noSlope();
     610         3240 :     case Pid::POSITION_LINKED_TO_MASTER:
     611              :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     612        13834 :         for (ChordRest* chordRest : elements()) {
     613        10594 :             bool linked = chordRest->getProperty(propertyId).toBool();
     614        10594 :             if (!linked) {
     615            0 :                 return false;
     616              :             }
     617              :         }
     618         3240 :         return true;
     619        14769 :     default:
     620        14769 :         return BeamBase::getProperty(propertyId);
     621              :     }
     622              : }
     623              : 
     624              : //---------------------------------------------------------
     625              : //   setProperty
     626              : //---------------------------------------------------------
     627              : 
     628         6150 : bool Beam::setProperty(Pid propertyId, const PropertyValue& v)
     629              : {
     630         6150 :     switch (propertyId) {
     631            1 :     case Pid::GROW_LEFT:
     632            1 :         setGrowLeft(v.toDouble());
     633            1 :         break;
     634            1 :     case Pid::GROW_RIGHT:
     635            1 :         setGrowRight(v.toDouble());
     636            1 :         break;
     637            0 :     case Pid::BEAM_POS:
     638            0 :         if (userModified()) {
     639            0 :             setBeamPos(v.value<PairF>());
     640              :         }
     641            0 :         break;
     642         2877 :     case Pid::BEAM_NO_SLOPE:
     643         2877 :         setNoSlope(v.toBool());
     644         2877 :         break;
     645            0 :     case Pid::POSITION_LINKED_TO_MASTER:
     646              :     case Pid::APPEARANCE_LINKED_TO_MASTER:
     647            0 :         if (v.toBool() == true) {
     648            0 :             for (ChordRest* chordRest : elements()) {
     649              :                 // when re-linking, re-link all the chords
     650            0 :                 chordRest->setProperty(propertyId, v);
     651              :             }
     652            0 :             resetProperty(Pid::STEM_DIRECTION);
     653            0 :             break;
     654              :         }
     655              :     // fall through
     656              :     default:
     657         3271 :         if (!BeamBase::setProperty(propertyId, v)) {
     658            0 :             return false;
     659              :         }
     660         3271 :         break;
     661              :     }
     662         6150 :     triggerLayout();
     663         6150 :     setGenerated(false);
     664         6150 :     return true;
     665              : }
     666              : 
     667              : //---------------------------------------------------------
     668              : //   propertyDefault
     669              : //---------------------------------------------------------
     670              : 
     671        23932 : PropertyValue Beam::propertyDefault(Pid id) const
     672              : {
     673        23932 :     switch (id) {
     674         1620 :     case Pid::GROW_LEFT:      return 1.0;
     675         1620 :     case Pid::GROW_RIGHT:     return 1.0;
     676            0 :     case Pid::BEAM_POS:       return PropertyValue::fromValue(beamPos());
     677        20692 :     default:                  return BeamBase::propertyDefault(id);
     678              :     }
     679              : }
     680              : 
     681              : //---------------------------------------------------------
     682              : //   addSkyline
     683              : //    add beam shape to skyline
     684              : //---------------------------------------------------------
     685              : 
     686         4211 : void Beam::addSkyline(Skyline& sk)
     687              : {
     688         4211 :     if (m_beamSegments.empty() || !addToSkyline()) {
     689            8 :         return;
     690              :     }
     691              : 
     692              :     // Only add the outer segment, no need to add the inner one
     693         4203 :     sk.add(m_beamSegments.front()->shape());
     694              : }
     695              : 
     696              : //---------------------------------------------------------
     697              : //   tick
     698              : //---------------------------------------------------------
     699              : 
     700        27769 : Fraction Beam::tick() const
     701              : {
     702        27769 :     return m_elements.empty() ? Fraction(0, 1) : m_elements.front()->segment()->tick();
     703              : }
     704              : 
     705              : //---------------------------------------------------------
     706              : //   rtick
     707              : //---------------------------------------------------------
     708              : 
     709            0 : Fraction Beam::rtick() const
     710              : {
     711            0 :     return m_elements.empty() ? Fraction(0, 1) : m_elements.front()->segment()->rtick();
     712              : }
     713              : 
     714              : //---------------------------------------------------------
     715              : //   ticks
     716              : //    calculate the ticks of all chords and rests connected by the beam
     717              : //---------------------------------------------------------
     718              : 
     719         4346 : Fraction Beam::ticks() const
     720              : {
     721         4346 :     Fraction ticks = Fraction(0, 1);
     722        19200 :     for (ChordRest* cr : m_elements) {
     723        14854 :         ticks += cr->actualTicks();
     724              :     }
     725         4346 :     return ticks;
     726              : }
     727              : 
     728              : //---------------------------------------------------------
     729              : //   actionIconTypeForBeamMode
     730              : //---------------------------------------------------------
     731              : 
     732            0 : ActionIconType Beam::actionIconTypeForBeamMode(BeamMode mode)
     733              : {
     734            0 :     switch (mode) {
     735            0 :     case BeamMode::AUTO:
     736            0 :         return ActionIconType::BEAM_AUTO;
     737            0 :     case BeamMode::NONE:
     738            0 :         return ActionIconType::BEAM_NONE;
     739            0 :     case BeamMode::BEGIN:
     740            0 :         return ActionIconType::BEAM_BREAK_LEFT;
     741            0 :     case BeamMode::BEGIN16:
     742            0 :         return ActionIconType::BEAM_BREAK_INNER_8TH;
     743            0 :     case BeamMode::BEGIN32:
     744            0 :         return ActionIconType::BEAM_BREAK_INNER_16TH;
     745            0 :     case BeamMode::MID:
     746            0 :         return ActionIconType::BEAM_JOIN;
     747            0 :     default:
     748            0 :         break;
     749              :     }
     750            0 :     return ActionIconType::UNDEFINED;
     751              : }
     752              : 
     753              : //---------------------------------------------------------
     754              : //   drag
     755              : //---------------------------------------------------------
     756              : 
     757            0 : RectF Beam::drag(EditData& ed)
     758              : {
     759            0 :     int idx = directionIdx();
     760            0 :     double dy = ed.pos.y() - ed.lastPos.y();
     761            0 :     BeamEditData* bed = static_cast<BeamEditData*>(ed.getData(this).get());
     762            0 :     BeamFragment* f = m_fragments[bed->editFragment];
     763              : 
     764            0 :     double y1 = f->py1[idx];
     765            0 :     double y2 = f->py2[idx];
     766              : 
     767            0 :     y1 += dy;
     768            0 :     y2 += dy;
     769              : 
     770            0 :     double _spatium = spatium();
     771              :     // Because of the logic in Beam::setProperty(),
     772              :     // changing Pid::BEAM_POS only has an effect if Pid::USER_MODIFIED is true.
     773            0 :     undoChangeProperty(Pid::USER_MODIFIED, true);
     774            0 :     undoChangeProperty(Pid::BEAM_POS, PairF(y1 / _spatium, y2 / _spatium));
     775            0 :     undoChangeProperty(Pid::GENERATED, false);
     776              : 
     777            0 :     triggerLayout();
     778              : 
     779            0 :     return canvasBoundingRect();
     780              : }
     781              : 
     782              : //---------------------------------------------------------
     783              : //   isMovable
     784              : //---------------------------------------------------------
     785            0 : bool Beam::isMovable() const
     786              : {
     787            0 :     return true;
     788              : }
     789              : 
     790              : //---------------------------------------------------------
     791              : //   initBeamEditData
     792              : //---------------------------------------------------------
     793            0 : void Beam::initBeamEditData(EditData& ed)
     794              : {
     795            0 :     std::shared_ptr<BeamEditData> bed = std::make_shared<BeamEditData>();
     796            0 :     bed->e    = this;
     797            0 :     bed->editFragment = 0;
     798            0 :     ed.addData(bed);
     799            0 : }
     800              : 
     801              : //---------------------------------------------------------
     802              : //   startDrag
     803              : //---------------------------------------------------------
     804            0 : void Beam::startDrag(EditData& editData)
     805              : {
     806            0 :     initBeamEditData(editData);
     807            0 : }
     808              : }
     809              : 
     810              : //---------------------------------------------------------
     811              : //   containsChord
     812              : //---------------------------------------------------------
     813         9272 : bool Beam::hasAllRests()
     814              : {
     815         9272 :     for (ChordRest* cr : m_elements) {
     816         9272 :         if (cr && cr->isChord()) {
     817         9272 :             return false;
     818              :         }
     819              :     }
     820            0 :     return true;
     821              : }
     822              : 
     823        11230 : void Beam::clearBeamSegments()
     824              : {
     825        41103 :     for (ChordRest* chordRest : m_elements) {
     826        29873 :         chordRest->setBeamlet(nullptr);
     827              :     }
     828              : 
     829        11230 :     BeamBase::clearBeamSegments();
     830        11230 : }
     831              : 
     832              : //-------------------------------------------------------
     833              : // BEAM SEGMENT CLASS
     834              : //-------------------------------------------------------
     835              : 
     836        13295 : BeamSegment::BeamSegment(EngravingItem* b)
     837        13295 :     : parentElement(b)
     838              : {
     839        13295 :     DO_ASSERT(parentElement->isType(ElementType::BEAM) || parentElement->isType(ElementType::TREMOLO_TWOCHORD));
     840        13295 : }
     841              : 
     842        18197 : Shape BeamSegment::shape() const
     843              : {
     844        18197 :     Shape shape;
     845        18197 :     PointF startPoint = line.p1();
     846        18197 :     PointF endPoint = line.p2();
     847        18197 :     double _beamWidth = parentElement->isBeam()
     848        18197 :                         ? item_cast<const Beam*>(parentElement)->m_beamWidth
     849            0 :                         : item_cast<const TremoloTwoChord*>(parentElement)->ldata()->beamWidth;
     850              :     // This is the case of right-beamlets
     851        18197 :     if (startPoint.x() > endPoint.x()) {
     852         1245 :         std::swap(startPoint, endPoint);
     853              :     }
     854        18197 :     double beamHorizontalLength = endPoint.x() - startPoint.x();
     855              :     // If beam is horizontal, one rectangle is enough
     856        18197 :     if (muse::RealIsEqual(startPoint.y(), endPoint.y())) {
     857        10922 :         RectF rect(startPoint.x(), startPoint.y(), beamHorizontalLength, _beamWidth / 2);
     858        10922 :         rect.adjust(0.0, -_beamWidth / 2, 0.0, 0.0);
     859        10922 :         shape.add(rect, parentElement);
     860        10922 :         return shape;
     861              :     }
     862              :     // If not, break the beam shape into multiple rectangles
     863         7275 :     double beamHeightDiff = endPoint.y() - startPoint.y();
     864         7275 :     int subBoxesCount = floor(beamHorizontalLength / parentElement->spatium());
     865         7275 :     subBoxesCount = std::max(subBoxesCount, 1); // at least one rectangle, of course (avoid division by zero)
     866         7275 :     double horizontalStep = beamHorizontalLength / subBoxesCount;
     867         7275 :     double verticalStep = beamHeightDiff / subBoxesCount;
     868         7275 :     std::vector<PointF> pointsOnBeamLine;
     869         7275 :     pointsOnBeamLine.reserve(subBoxesCount);
     870         7275 :     pointsOnBeamLine.push_back(startPoint);
     871        52139 :     for (int i = 0; i < subBoxesCount - 1; ++i) {
     872        44864 :         PointF nextPoint = pointsOnBeamLine.back() + PointF(horizontalStep, verticalStep);
     873        44864 :         pointsOnBeamLine.push_back(nextPoint);
     874              :     }
     875        59414 :     for (PointF point : pointsOnBeamLine) {
     876        52139 :         RectF rect(point.x(), point.y(), horizontalStep, _beamWidth / 2);
     877        52139 :         rect.adjust(0.0, -_beamWidth / 2, 0.0, 0.0);
     878        52139 :         shape.add(rect, parentElement);
     879              :     }
     880         7275 :     return shape;
     881         7275 : }
        

Generated by: LCOV version 2.0-1