LCOV - code coverage report
Current view: top level - src/engraving/dom - chordline.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 59.9 % 197 118
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 "chordline.h"
      24              : 
      25              : #include <functional>
      26              : 
      27              : #include "types/translatablestring.h"
      28              : 
      29              : #include "../editing/elementeditdata.h"
      30              : #include "../types/typesconv.h"
      31              : 
      32              : #include "chord.h"
      33              : #include "note.h"
      34              : 
      35              : #include "log.h"
      36              : 
      37              : using namespace mu;
      38              : using namespace muse::draw;
      39              : using namespace mu::engraving;
      40              : 
      41              : namespace mu::engraving {
      42              : //---------------------------------------------------------
      43              : //   ChordLine
      44              : //---------------------------------------------------------
      45              : 
      46          159 : ChordLine::ChordLine(DummyParentOr<Chord> parent)
      47          159 :     : EngravingItem(ElementType::CHORDLINE, parent, ElementFlag::MOVABLE)
      48              : {
      49          159 : }
      50              : 
      51            8 : ChordLine::ChordLine(const ChordLine& cl)
      52            8 :     : EngravingItem(cl)
      53              : {
      54            8 :     m_chordLineType = cl.m_chordLineType;
      55            8 :     m_straight = cl.m_straight;
      56            8 :     m_wavy = cl.m_wavy;
      57            8 :     m_bezierOffsets = cl.m_bezierOffsets;
      58            8 :     m_playChordLine = cl.m_playChordLine;
      59            8 :     m_note = cl.m_note;
      60            8 : }
      61              : 
      62              : //---------------------------------------------------------
      63              : //   setChordLineType
      64              : //---------------------------------------------------------
      65              : 
      66          164 : void ChordLine::setChordLineType(ChordLineType st)
      67              : {
      68          164 :     m_chordLineType = st;
      69          164 : }
      70              : 
      71            0 : const TranslatableString& ChordLine::chordLineTypeName() const
      72              : {
      73            0 :     return TConv::userName(m_chordLineType, m_straight, m_wavy);
      74              : }
      75              : 
      76          178 : ChordLine::Points ChordLine::defaultPoints() const
      77              : {
      78          178 :     const Spatium x = isToTheLeft() ? -1.2_sp : 1.2_sp; // let the symbols extend a bit more horizontally
      79          178 :     const Spatium y = isBelow() ? 1.0_sp : -1.0_sp;
      80              : 
      81          178 :     if (isToTheLeft()) {
      82          154 :         return { PointF(), PointF(0.0, (y / 2).val()), PointF((x / 2).val(), y.val()), PointF(x.val(), y.val()) };
      83              :     }
      84              : 
      85          202 :     return { PointF(), PointF((x / 2).val(), 0.0), PointF(x.val(), (y / 2).val()), PointF(x.val(), y.val()) };
      86              : }
      87              : 
      88          178 : double ChordLine::chordMag() const
      89              : {
      90          178 :     const EngravingObject* parent = ownershipParent();
      91          178 :     return parent && parent->isChord() ? toChord(parent)->intrinsicMag() : 1.0;
      92              : }
      93              : 
      94          410 : Pid ChordLine::bezierOffsetPid(Point point)
      95              : {
      96          410 :     switch (point) {
      97          102 :     case Point::START: return Pid::BEZIER_OFFSET1;
      98          102 :     case Point::BEZIER1: return Pid::BEZIER_OFFSET2;
      99          102 :     case Point::BEZIER2: return Pid::BEZIER_OFFSET3;
     100          104 :     case Point::END: return Pid::BEZIER_OFFSET4;
     101            0 :     case Point::POINTS_COUNT: break;
     102              :     }
     103              : 
     104            0 :     UNREACHABLE;
     105              :     return Pid::END;
     106              : }
     107              : 
     108            2 : ChordLine::Point ChordLine::gripPoint(Grip grip) const
     109              : {
     110              :     // straight lines only have one grip, for the end point
     111            2 :     return m_straight ? Point::END : Point(static_cast<int>(grip));
     112              : }
     113              : 
     114            6 : int ChordLine::gripsCount() const
     115              : {
     116            6 :     if (m_wavy || m_chordLineType == ChordLineType::NOTYPE) {
     117            2 :         return 0;
     118              :     }
     119              : 
     120            4 :     return m_straight ? 1 : static_cast<int>(Point::POINTS_COUNT);
     121              : }
     122              : 
     123              : //---------------------------------------------------------
     124              : //   startDragGrip
     125              : //---------------------------------------------------------
     126              : 
     127            0 : void ChordLine::startDragGrip(EditData& ed)
     128              : {
     129            0 :     EngravingItem::startDragGrip(ed);
     130            0 :     ElementEditDataPtr eed = ed.getData(this);
     131              : 
     132            0 :     for (size_t i = 0; i < size_t(Point::POINTS_COUNT); ++i) {
     133            0 :         eed->pushProperty(bezierOffsetPid(Point(i)));
     134              :     }
     135            0 : }
     136              : 
     137              : //---------------------------------------------------------
     138              : //   dragGrip
     139              : //---------------------------------------------------------
     140              : 
     141            3 : void ChordLine::dragGrip(EditData& ed)
     142              : {
     143            3 :     if (m_wavy || ed.curGrip == Grip::NO_GRIP || int(ed.curGrip) >= gripsCount()) {
     144            1 :         return;
     145              :     }
     146              : 
     147            2 :     const Point point = gripPoint(ed.curGrip);
     148              :     // offsets are at the chord's normal size, the drawn shape is scaled by its mag
     149            2 :     const PointF offset = bezierOffset(point) + ed.delta / chordMag();
     150              : 
     151            2 :     if (m_straight) {
     152              :         // stop the user from being able to turn one kind of slide into another
     153              :         // (the shape at the chord's normal size is enough to check the direction)
     154            0 :         const Points points = defaultPoints();
     155            0 :         const double sp = spatium();
     156            0 :         const PointF start = points[size_t(Point::START)] * sp + bezierOffset(Point::START);
     157            0 :         const PointF end = points[size_t(Point::END)] * sp + offset;
     158            0 :         if ((m_chordLineType == ChordLineType::FALL && (end.x() < start.x() || end.y() < start.y()))
     159            0 :             || (m_chordLineType == ChordLineType::DOIT && (end.x() < start.x() || end.y() > start.y()))
     160            0 :             || (m_chordLineType == ChordLineType::SCOOP && (end.x() > start.x() || end.y() < start.y()))
     161            0 :             || (m_chordLineType == ChordLineType::PLOP && (end.x() > start.x() || end.y() > start.y()))) {
     162            0 :             return;
     163              :         }
     164              :     }
     165              : 
     166            2 :     setBezierOffset(point, offset);
     167            2 :     triggerLayout();
     168              : }
     169              : 
     170              : //---------------------------------------------------------
     171              : //   gripsPositions
     172              : //---------------------------------------------------------
     173              : 
     174            0 : std::vector<PointF> ChordLine::gripsPositions(const EditData&) const
     175              : {
     176            0 :     if (m_wavy) {
     177            0 :         NOT_IMPLEMENTED;
     178            0 :         return {};
     179              :     }
     180              : 
     181            0 :     const PainterPath& path = ldata()->path;
     182              : 
     183            0 :     size_t n = path.elementCount();
     184            0 :     if (n != (m_straight ? 2 : size_t(Point::POINTS_COUNT))) {
     185            0 :         return {};
     186              :     }
     187              : 
     188            0 :     PointF cp(pagePos());
     189            0 :     if (m_straight) {
     190              :         // limit the number of grips to one
     191            0 :         double offset = 0.5;
     192            0 :         PointF p;
     193              : 
     194            0 :         if (m_chordLineType == ChordLineType::FALL) {
     195            0 :             p = PointF(offset, -offset);
     196            0 :         } else if (m_chordLineType == ChordLineType::DOIT) {
     197            0 :             p = PointF(offset, offset);
     198            0 :         } else if (m_chordLineType == ChordLineType::SCOOP) {
     199            0 :             p = PointF(-offset, offset);
     200            0 :         } else if (m_chordLineType == ChordLineType::PLOP) {
     201            0 :             p = PointF(-offset, -offset);
     202              :         }
     203              : 
     204              :         // translate on the length and height - stops the grips from going past boundaries of slide
     205            0 :         p += (cp + PointF(path.elementAt(1).x, path.elementAt(1).y));
     206            0 :         return { p };
     207              :     } else {
     208            0 :         std::vector<PointF> grips(n);
     209            0 :         for (size_t i = 0; i < n; ++i) {
     210            0 :             grips[i] = cp + PointF(path.elementAt(i).x, path.elementAt(i).y);
     211              :         }
     212            0 :         return grips;
     213            0 :     }
     214              : }
     215              : 
     216              : //---------------------------------------------------------
     217              : //   slideType
     218              : //---------------------------------------------------------
     219              : 
     220          119 : static Note::SlideType slideType(ChordLineType type)
     221              : {
     222              :     static const std::unordered_map<ChordLineType, Note::SlideType> chordLineToSlideTypes {
     223              :         { ChordLineType::FALL, Note::SlideType::DownFromNote },
     224              :         { ChordLineType::DOIT, Note::SlideType::UpFromNote },
     225              :         { ChordLineType::SCOOP, Note::SlideType::UpToNote },
     226              :         { ChordLineType::PLOP, Note::SlideType::DownToNote }
     227          125 :     };
     228              : 
     229          119 :     auto it = chordLineToSlideTypes.find(type);
     230          119 :     if (it != chordLineToSlideTypes.end()) {
     231          119 :         return it->second;
     232              :     }
     233              : 
     234            0 :     return Note::SlideType::Undefined;
     235              : }
     236              : 
     237              : //---------------------------------------------------------
     238              : //   setNote
     239              : //---------------------------------------------------------
     240              : 
     241          119 : void ChordLine::setNote(Note* note)
     242              : {
     243          119 :     m_note = note;
     244              : 
     245          119 :     if (note) {
     246          119 :         note->attachSlide(slideType(m_chordLineType));
     247              :     }
     248          119 : }
     249              : 
     250            6 : SymId ChordLine::waveSym() const
     251              : {
     252            6 :     if (m_chordLineType == ChordLineType::FALL || m_chordLineType == ChordLineType::PLOP) {
     253            4 :         return SymId::brassFallRoughShort;
     254              :     }
     255              : 
     256            2 :     return SymId::brassLiftShort;
     257              : }
     258              : 
     259              : //---------------------------------------------------------
     260              : //   accessibleInfo
     261              : //---------------------------------------------------------
     262              : 
     263            0 : String ChordLine::accessibleInfo() const
     264              : {
     265            0 :     String rez = EngravingItem::accessibleInfo();
     266            0 :     if (chordLineType() != ChordLineType::NOTYPE) {
     267            0 :         rez = String(u"%1: %2").arg(rez, chordLineTypeName().translated());
     268              :     }
     269            0 :     return rez;
     270            0 : }
     271              : 
     272            0 : int ChordLine::subtype() const
     273              : {
     274            0 :     size_t h1 = std::hash<ChordLineType> {}(m_chordLineType);
     275            0 :     size_t h2 = std::hash<bool> {}(m_straight);
     276            0 :     size_t h3 = std::hash<bool> {}(m_wavy);
     277              : 
     278            0 :     return static_cast<int>(h1 ^ (h2 << 1) ^ (h3 << 2));
     279              : }
     280              : 
     281            0 : muse::TranslatableString ChordLine::subtypeUserName() const
     282              : {
     283            0 :     return chordLineTypeName();
     284              : }
     285              : 
     286              : //---------------------------------------------------------
     287              : //   getProperty
     288              : //---------------------------------------------------------
     289              : 
     290         1674 : PropertyValue ChordLine::getProperty(Pid propertyId) const
     291              : {
     292         1674 :     switch (propertyId) {
     293           99 :     case Pid::BEZIER_OFFSET1:
     294           99 :         return bezierOffset(Point::START);
     295           99 :     case Pid::BEZIER_OFFSET2:
     296           99 :         return bezierOffset(Point::BEZIER1);
     297           99 :     case Pid::BEZIER_OFFSET3:
     298           99 :         return bezierOffset(Point::BEZIER2);
     299          108 :     case Pid::BEZIER_OFFSET4:
     300          108 :         return bezierOffset(Point::END);
     301           97 :     case Pid::CHORD_LINE_TYPE:
     302           97 :         return int(m_chordLineType);
     303           97 :     case Pid::CHORD_LINE_STRAIGHT:
     304           97 :         return m_straight;
     305           97 :     case Pid::CHORD_LINE_WAVY:
     306           97 :         return m_wavy;
     307           97 :     case Pid::PLAY:
     308           97 :         return m_playChordLine;
     309          881 :     default:
     310          881 :         break;
     311              :     }
     312          881 :     return EngravingItem::getProperty(propertyId);
     313              : }
     314              : 
     315              : //---------------------------------------------------------
     316              : //   setProperty
     317              : //---------------------------------------------------------
     318              : 
     319           10 : bool ChordLine::setProperty(Pid propertyId, const PropertyValue& val)
     320              : {
     321           10 :     switch (propertyId) {
     322            1 :     case Pid::BEZIER_OFFSET1:
     323            1 :         setBezierOffset(Point::START, val.value<PointF>());
     324            1 :         break;
     325            2 :     case Pid::BEZIER_OFFSET2:
     326            2 :         setBezierOffset(Point::BEZIER1, val.value<PointF>());
     327            2 :         break;
     328            1 :     case Pid::BEZIER_OFFSET3:
     329            1 :         setBezierOffset(Point::BEZIER2, val.value<PointF>());
     330            1 :         break;
     331            6 :     case Pid::BEZIER_OFFSET4:
     332            6 :         setBezierOffset(Point::END, val.value<PointF>());
     333            6 :         break;
     334            0 :     case Pid::CHORD_LINE_TYPE:
     335            0 :         setChordLineType(ChordLineType(val.toInt()));
     336            0 :         break;
     337            0 :     case Pid::CHORD_LINE_STRAIGHT:
     338            0 :         setStraight(val.toBool());
     339            0 :         break;
     340            0 :     case Pid::CHORD_LINE_WAVY:
     341            0 :         setWavy(val.toBool());
     342            0 :         break;
     343            0 :     case Pid::PLAY:
     344            0 :         setPlayChordLine(val.toBool());
     345            0 :         break;
     346            0 :     default:
     347            0 :         return EngravingItem::setProperty(propertyId, val);
     348              :     }
     349           10 :     triggerLayout();
     350           10 :     return true;
     351              : }
     352              : 
     353              : //---------------------------------------------------------
     354              : //   propertyDefault
     355              : //---------------------------------------------------------
     356              : 
     357         1662 : PropertyValue ChordLine::propertyDefault(Pid pid) const
     358              : {
     359         1662 :     switch (pid) {
     360           97 :     case Pid::CHORD_LINE_STRAIGHT:
     361           97 :         return false;
     362           97 :     case Pid::CHORD_LINE_WAVY:
     363           97 :         return false;
     364           97 :     case Pid::PLAY:
     365           97 :         return true;
     366          396 :     case Pid::BEZIER_OFFSET1:
     367              :     case Pid::BEZIER_OFFSET2:
     368              :     case Pid::BEZIER_OFFSET3:
     369              :     case Pid::BEZIER_OFFSET4:
     370          792 :         return PointF();
     371          975 :     default:
     372          975 :         break;
     373              :     }
     374          975 :     return EngravingItem::propertyDefault(pid);
     375              : }
     376              : 
     377              : //---------------------------------------------------------
     378              : //   reset
     379              : //---------------------------------------------------------
     380              : 
     381            1 : void ChordLine::reset()
     382              : {
     383            5 :     for (size_t i = 0; i < size_t(Point::POINTS_COUNT); ++i) {
     384            4 :         undoResetProperty(bezierOffsetPid(Point(i)));
     385              :     }
     386            1 :     EngravingItem::reset();
     387            1 : }
     388              : 
     389              : //---------------------------------------------------------
     390              : //   spatiumChanged
     391              : //---------------------------------------------------------
     392              : 
     393            2 : void ChordLine::spatiumChanged(double oldValue, double newValue)
     394              : {
     395            2 :     EngravingItem::spatiumChanged(oldValue, newValue);
     396           10 :     for (PointF& offset : m_bezierOffsets) {
     397            8 :         offset *= newValue / oldValue;
     398              :     }
     399            2 : }
     400              : 
     401              : //---------------------------------------------------------
     402              : //   localSpatiumChanged
     403              : //---------------------------------------------------------
     404              : 
     405            0 : void ChordLine::localSpatiumChanged(double oldValue, double newValue)
     406              : {
     407            0 :     EngravingItem::localSpatiumChanged(oldValue, newValue);
     408            0 :     for (PointF& offset : m_bezierOffsets) {
     409            0 :         offset *= newValue / oldValue;
     410              :     }
     411            0 : }
     412              : }
        

Generated by: LCOV version 2.0-1