2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
2016-11-15 16:33:27 +01:00
// Copyright (C) 2002-2016 Werner Schweer
2012-05-26 14:26:10 +02:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
# include "measure.h"
# include "score.h"
# include "system.h"
# include "undo.h"
2016-11-15 16:33:27 +01:00
# include "chord.h"
2012-05-26 14:26:10 +02:00
# include "stem.h"
2016-11-15 16:33:27 +01:00
# include "slur.h"
# include "tie.h"
2013-10-13 11:36:08 +02:00
# include "part.h"
2016-11-15 16:33:27 +01:00
# include "navigate.h"
2017-01-05 11:23:47 +01:00
# include "articulation.h"
2012-05-26 14:26:10 +02:00
2013-05-13 18:49:17 +02:00
namespace Ms {
2014-07-31 16:30:04 +02:00
Element * SlurTie : : editEndElement ;
Element * SlurTie : : editStartElement ;
QList < SlurOffsets > SlurTie : : editUps ;
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void SlurSegment : : draw ( QPainter * painter ) const
{
QPen pen ( curColor ( ) ) ;
2013-06-10 11:03:34 +02:00
switch ( slurTie ( ) - > lineType ( ) ) {
case 0 :
2014-05-07 12:10:28 +02:00
painter - > setBrush ( QBrush ( pen . color ( ) ) ) ;
2013-06-10 11:03:34 +02:00
pen . setCapStyle ( Qt : : RoundCap ) ;
pen . setJoinStyle ( Qt : : RoundJoin ) ;
2018-03-27 15:36:00 +02:00
pen . setWidthF ( score ( ) - > styleP ( Sid : : SlurEndWidth ) ) ;
2013-06-10 11:03:34 +02:00
break ;
case 1 :
painter - > setBrush ( Qt : : NoBrush ) ;
2018-03-27 15:36:00 +02:00
pen . setWidthF ( score ( ) - > styleP ( Sid : : SlurDottedWidth ) ) ;
2013-06-10 11:03:34 +02:00
pen . setStyle ( Qt : : DotLine ) ;
break ;
case 2 :
painter - > setBrush ( Qt : : NoBrush ) ;
2018-03-27 15:36:00 +02:00
pen . setWidthF ( score ( ) - > styleP ( Sid : : SlurDottedWidth ) ) ;
2013-06-10 11:03:34 +02:00
pen . setStyle ( Qt : : DashLine ) ;
break ;
2017-04-13 13:54:12 +02:00
case 3 :
painter - > setBrush ( Qt : : NoBrush ) ;
2018-03-27 15:36:00 +02:00
pen . setWidthF ( score ( ) - > styleP ( Sid : : SlurDottedWidth ) ) ;
2017-04-13 13:54:12 +02:00
pen . setStyle ( Qt : : CustomDashLine ) ;
QVector < qreal > dashes { 5.0 , 5.0 } ;
pen . setDashPattern ( dashes ) ;
break ;
2012-07-07 12:14:48 +02:00
}
2012-05-26 14:26:10 +02:00
painter - > setPen ( pen ) ;
painter - > drawPath ( path ) ;
}
//---------------------------------------------------------
// updateGrips
// return grip rectangles in page coordinates
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
void SlurSegment : : updateGrips ( EditData & ed ) const
2012-05-26 14:26:10 +02:00
{
QPointF p ( pagePos ( ) ) ;
2017-07-05 17:41:47 +02:00
for ( int i = 0 ; i < ed . grips ; + + i )
2017-03-31 13:03:15 +02:00
ed . grip [ i ] . translate ( _ups [ i ] . p + _ups [ i ] . off + p ) ;
2012-05-26 14:26:10 +02:00
}
2013-10-13 11:36:08 +02:00
//---------------------------------------------------------
// searchCR
//---------------------------------------------------------
static ChordRest * searchCR ( Segment * segment , int startTrack , int endTrack )
{
2017-03-08 13:12:26 +01:00
// for (Segment* s = segment; s; s = s->next1MM(SegmentType::ChordRest)) {
for ( Segment * s = segment ; s ; s = s - > next ( SegmentType : : ChordRest ) ) { // restrict search to measure
2013-10-13 11:36:08 +02:00
if ( startTrack > endTrack ) {
for ( int t = startTrack - 1 ; t > = endTrack ; - - t ) {
if ( s - > element ( t ) )
2016-07-04 13:21:33 +02:00
return toChordRest ( s - > element ( t ) ) ;
2013-10-13 11:36:08 +02:00
}
}
else {
for ( int t = startTrack ; t < endTrack ; + + t ) {
if ( s - > element ( t ) )
2016-07-04 13:21:33 +02:00
return toChordRest ( s - > element ( t ) ) ;
2013-10-13 11:36:08 +02:00
}
}
}
return 0 ;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// edit
// return true if event is accepted
//---------------------------------------------------------
2017-03-31 13:03:15 +02:00
bool SlurSegment : : edit ( EditData & ed )
2012-05-26 14:26:10 +02:00
{
2016-11-15 16:33:27 +01:00
Slur * sl = slur ( ) ;
2012-05-26 14:26:10 +02:00
2017-03-31 13:03:15 +02:00
if ( ed . key = = Qt : : Key_X ) {
2018-03-27 15:36:00 +02:00
sl - > undoChangeProperty ( Pid : : SLUR_DIRECTION , QVariant : : fromValue < Direction > ( sl - > up ( ) ? Direction : : DOWN : Direction : : UP ) ) ;
2012-05-26 14:26:10 +02:00
sl - > layout ( ) ;
return true ;
}
2017-03-31 13:03:15 +02:00
if ( ed . key = = Qt : : Key_Home ) {
2017-07-26 09:59:24 +02:00
ups ( ed . curGrip ) . off = QPointF ( ) ; //TODO
2013-10-16 18:09:26 +02:00
sl - > layout ( ) ;
return true ;
}
2012-05-26 14:26:10 +02:00
2017-03-31 13:03:15 +02:00
if ( ! ( ( ed . modifiers & Qt : : ShiftModifier ) & & ( isSingleType ( ) | | ( isBeginType ( ) & & ed . curGrip = = Grip : : START ) | | ( isEndType ( ) & & ed . curGrip = = Grip : : END ) ) ) )
2012-05-26 14:26:10 +02:00
return false ;
ChordRest * cr = 0 ;
2016-07-04 13:21:33 +02:00
ChordRest * e ;
ChordRest * e1 ;
2017-03-31 13:03:15 +02:00
if ( ed . curGrip = = Grip : : START ) {
2017-07-26 09:59:24 +02:00
e = sl - > startCR ( ) ;
2016-07-04 13:21:33 +02:00
e1 = sl - > endCR ( ) ;
}
else {
2017-07-26 09:59:24 +02:00
e = sl - > endCR ( ) ;
2016-07-04 13:21:33 +02:00
e1 = sl - > startCR ( ) ;
}
2012-05-26 14:26:10 +02:00
2017-03-31 13:03:15 +02:00
if ( ed . key = = Qt : : Key_Left )
2013-06-12 14:23:57 +02:00
cr = prevChordRest ( e ) ;
2017-03-31 13:03:15 +02:00
else if ( ed . key = = Qt : : Key_Right )
2013-06-12 14:23:57 +02:00
cr = nextChordRest ( e ) ;
2017-03-31 13:03:15 +02:00
else if ( ed . key = = Qt : : Key_Up ) {
2015-03-13 11:16:43 +01:00
Part * part = e - > part ( ) ;
2013-10-13 11:36:08 +02:00
int startTrack = part - > startTrack ( ) ;
int endTrack = e - > track ( ) ;
cr = searchCR ( e - > segment ( ) , endTrack , startTrack ) ;
}
2017-03-31 13:03:15 +02:00
else if ( ed . key = = Qt : : Key_Down ) {
2013-10-13 11:36:08 +02:00
int startTrack = e - > track ( ) + 1 ;
2015-03-13 11:16:43 +01:00
Part * part = e - > part ( ) ;
2013-10-13 11:36:08 +02:00
int endTrack = part - > endTrack ( ) ;
cr = searchCR ( e - > segment ( ) , startTrack , endTrack ) ;
}
2014-07-10 14:32:04 +02:00
if ( cr & & cr ! = e1 )
2017-07-26 09:59:24 +02:00
changeAnchor ( ed , cr ) ;
2012-05-26 14:26:10 +02:00
return true ;
}
//---------------------------------------------------------
// changeAnchor
//---------------------------------------------------------
2017-07-26 09:59:24 +02:00
void SlurSegment : : changeAnchor ( EditData & ed , Element * element )
2012-05-26 14:26:10 +02:00
{
2017-07-26 09:59:24 +02:00
if ( ed . curGrip = = Grip : : START ) {
int ticks = spanner ( ) - > endElement ( ) - > tick ( ) - element - > tick ( ) ;
2018-03-27 15:36:00 +02:00
spanner ( ) - > undoChangeProperty ( Pid : : SPANNER_TICK , element - > tick ( ) ) ;
spanner ( ) - > undoChangeProperty ( Pid : : SPANNER_TICKS , ticks ) ;
2018-12-27 20:19:15 +01:00
int diff = element - > track ( ) - spanner ( ) - > track ( ) ;
for ( auto e : spanner ( ) - > linkList ( ) ) {
Spanner * s = toSpanner ( e ) ;
s - > undoChangeProperty ( Pid : : TRACK , s - > track ( ) + diff ) ;
}
2017-07-26 09:59:24 +02:00
if ( score ( ) - > spannerMap ( ) . removeSpanner ( spanner ( ) ) )
score ( ) - > addSpanner ( spanner ( ) ) ;
2012-05-26 14:26:10 +02:00
}
else {
2018-03-27 15:36:00 +02:00
spanner ( ) - > undoChangeProperty ( Pid : : SPANNER_TICKS , element - > tick ( ) - spanner ( ) - > startElement ( ) - > tick ( ) ) ;
2018-12-27 20:19:15 +01:00
int diff = element - > track ( ) - spanner ( ) - > track ( ) ;
for ( auto e : spanner ( ) - > linkList ( ) ) {
Spanner * s = toSpanner ( e ) ;
s - > undoChangeProperty ( Pid : : SPANNER_TRACK2 , s - > track ( ) + diff ) ;
}
2012-05-26 14:26:10 +02:00
}
2018-11-25 23:35:00 +01:00
const size_t segments = spanner ( ) - > spannerSegments ( ) . size ( ) ;
2017-07-26 09:59:24 +02:00
ups ( ed . curGrip ) . off = QPointF ( ) ;
2013-08-22 12:18:14 +02:00
spanner ( ) - > layout ( ) ;
if ( spanner ( ) - > spannerSegments ( ) . size ( ) ! = segments ) {
2018-11-25 23:35:00 +01:00
const std : : vector < SpannerSegment * > & ss = spanner ( ) - > spannerSegments ( ) ;
2017-07-26 09:59:24 +02:00
SlurSegment * newSegment = toSlurSegment ( ed . curGrip = = Grip : : END ? ss . back ( ) : ss . front ( ) ) ;
ed . view - > startEdit ( newSegment , ed . curGrip ) ;
2017-07-05 17:41:47 +02:00
triggerLayout ( ) ;
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// computeBezier
// compute help points of slur bezier segment
//---------------------------------------------------------
2016-11-15 16:33:27 +01:00
void SlurSegment : : computeBezier ( QPointF p6o )
2012-05-26 14:26:10 +02:00
{
qreal _spatium = spatium ( ) ;
qreal shoulderW ; // height as fraction of slur-length
qreal shoulderH ;
//
2017-08-02 18:19:08 +02:00
// pp1 and pp2 are the end points of the slur
2012-05-26 14:26:10 +02:00
//
2016-11-15 16:33:27 +01:00
QPointF pp1 = ups ( Grip : : START ) . p + ups ( Grip : : START ) . off ;
QPointF pp2 = ups ( Grip : : END ) . p + ups ( Grip : : END ) . off ;
2012-05-26 14:26:10 +02:00
QPointF p2 = pp2 - pp1 ;
2012-09-17 12:17:54 +02:00
if ( ( p2 . x ( ) = = 0.0 ) & & ( p2 . y ( ) = = 0.0 ) ) {
2016-11-15 16:33:27 +01:00
Measure * m1 = slur ( ) - > startCR ( ) - > segment ( ) - > measure ( ) ;
Measure * m2 = slur ( ) - > endCR ( ) - > segment ( ) - > measure ( ) ;
2016-07-15 19:57:02 +02:00
qDebug ( " zero slur at tick %d(%d) track %d in measure %d-%d tick %d ticks %d " ,
2016-11-15 16:33:27 +01:00
m1 - > tick ( ) , tick ( ) , track ( ) , m1 - > no ( ) , m2 - > no ( ) , slur ( ) - > tick ( ) , slur ( ) - > ticks ( ) ) ;
slur ( ) - > setBroken ( true ) ;
2012-05-26 14:26:10 +02:00
return ;
}
qreal sinb = atan ( p2 . y ( ) / p2 . x ( ) ) ;
QTransform t ;
t . rotateRadians ( - sinb ) ;
p2 = t . map ( p2 ) ;
p6o = t . map ( p6o ) ;
double smallH = 0.5 ;
qreal d = p2 . x ( ) / _spatium ;
if ( d < = 2.0 ) {
shoulderH = d * 0.5 * smallH * _spatium ;
shoulderW = .6 ;
}
else {
qreal dd = log10 ( 1.0 + ( d - 2.0 ) * .5 ) * 2.0 ;
if ( dd > 3.0 )
dd = 3.0 ;
shoulderH = ( dd + smallH ) * _spatium ;
if ( d > 18.0 )
2012-11-11 14:24:02 +01:00
shoulderW = 0.7 ; // 0.8;
2012-05-26 14:26:10 +02:00
else if ( d > 10 )
2012-11-11 14:24:02 +01:00
shoulderW = 0.6 ; // 0.7;
2012-05-26 14:26:10 +02:00
else
2012-11-11 14:24:02 +01:00
shoulderW = 0.5 ; // 0.6;
2012-05-26 14:26:10 +02:00
}
2012-09-15 14:13:44 +02:00
shoulderH - = p6o . y ( ) ;
2016-11-15 16:33:27 +01:00
if ( ! slur ( ) - > up ( ) )
2012-05-26 14:26:10 +02:00
shoulderH = - shoulderH ;
qreal c = p2 . x ( ) ;
qreal c1 = ( c - c * shoulderW ) * .5 + p6o . x ( ) ;
qreal c2 = c1 + c * shoulderW + p6o . x ( ) ;
QPointF p5 = QPointF ( c * .5 , 0.0 ) ;
QPointF p3 ( c1 , - shoulderH ) ;
QPointF p4 ( c2 , - shoulderH ) ;
2018-03-27 15:36:00 +02:00
qreal w = score ( ) - > styleP ( Sid : : SlurMidWidth ) - score ( ) - > styleP ( Sid : : SlurEndWidth ) ;
2014-10-04 13:39:06 +02:00
if ( ( c2 - c1 ) < = _spatium )
2012-05-26 14:26:10 +02:00
w * = .5 ;
QPointF th ( 0.0 , w ) ; // thickness of slur
2016-11-15 16:33:27 +01:00
QPointF p3o = p6o + t . map ( ups ( Grip : : BEZIER1 ) . off ) ;
QPointF p4o = p6o + t . map ( ups ( Grip : : BEZIER2 ) . off ) ;
2012-05-26 14:26:10 +02:00
2013-06-10 11:03:34 +02:00
if ( ! p6o . isNull ( ) ) {
2016-11-15 16:33:27 +01:00
QPointF p6i = t . inverted ( ) . map ( p6o ) ;
ups ( Grip : : BEZIER1 ) . off + = p6i ;
ups ( Grip : : BEZIER2 ) . off + = p6i ;
2012-09-15 14:13:44 +02:00
}
2012-05-26 14:26:10 +02:00
//-----------------------------------calculate p6
QPointF pp3 = p3 + p3o ;
QPointF pp4 = p4 + p4o ;
QPointF ppp4 = pp4 - pp3 ;
qreal r2 = atan ( ppp4 . y ( ) / ppp4 . x ( ) ) ;
t . reset ( ) ;
t . rotateRadians ( - r2 ) ;
QPointF p6 = QPointF ( t . map ( ppp4 ) . x ( ) * .5 , 0.0 ) ;
t . rotateRadians ( 2 * r2 ) ;
2012-09-15 14:13:44 +02:00
p6 = t . map ( p6 ) + pp3 - p6o ;
2012-05-26 14:26:10 +02:00
//-----------------------------------
2016-11-15 16:33:27 +01:00
path = QPainterPath ( ) ;
path . moveTo ( QPointF ( ) ) ;
path . cubicTo ( p3 + p3o - th , p4 + p4o - th , p2 ) ;
if ( slur ( ) - > lineType ( ) = = 0 )
path . cubicTo ( p4 + p4o + th , p3 + p3o + th , QPointF ( ) ) ;
2012-05-26 14:26:10 +02:00
th = QPointF ( 0.0 , 3.0 * w ) ;
2016-11-15 16:33:27 +01:00
shapePath = QPainterPath ( ) ;
shapePath . moveTo ( QPointF ( ) ) ;
shapePath . cubicTo ( p3 + p3o - th , p4 + p4o - th , p2 ) ;
shapePath . cubicTo ( p4 + p4o + th , p3 + p3o + th , QPointF ( ) ) ;
2012-05-26 14:26:10 +02:00
// translate back
t . reset ( ) ;
t . translate ( pp1 . x ( ) , pp1 . y ( ) ) ;
t . rotateRadians ( sinb ) ;
2016-11-15 16:33:27 +01:00
path = t . map ( path ) ;
shapePath = t . map ( shapePath ) ;
ups ( Grip : : BEZIER1 ) . p = t . map ( p3 ) ;
ups ( Grip : : BEZIER2 ) . p = t . map ( p4 ) ;
ups ( Grip : : END ) . p = t . map ( p2 ) - ups ( Grip : : END ) . off ;
ups ( Grip : : DRAG ) . p = t . map ( p5 ) ;
ups ( Grip : : SHOULDER ) . p = t . map ( p6 ) ;
2014-11-06 10:44:57 +01:00
2016-11-15 16:33:27 +01:00
_shape . clear ( ) ;
2017-08-02 18:19:08 +02:00
QPointF start = pp1 ;
int nbShapes = 32 ; // (pp2.x() - pp1.x()) / _spatium;
qreal minH = qAbs ( 3 * w ) ;
2019-02-12 10:20:19 +01:00
const CubicBezier b ( pp1 , ups ( Grip : : BEZIER1 ) . pos ( ) , ups ( Grip : : BEZIER2 ) . pos ( ) , ups ( Grip : : END ) . pos ( ) ) ;
2016-11-05 19:46:39 +01:00
for ( int i = 1 ; i < = nbShapes ; i + + ) {
2019-02-12 10:20:19 +01:00
const QPointF point = b . pointAtPercent ( i / float ( nbShapes ) ) ;
2017-08-02 18:19:08 +02:00
QRectF re = QRectF ( start , point ) . normalized ( ) ;
2017-07-10 15:22:51 +02:00
if ( re . height ( ) < minH ) {
2018-09-25 15:55:08 +02:00
qreal d1 = ( minH - re . height ( ) ) * .5 ;
re . adjust ( 0.0 , - d1 , 0.0 , d1 ) ;
2017-07-10 15:22:51 +02:00
}
2016-11-15 16:33:27 +01:00
_shape . add ( re ) ;
2016-11-05 19:46:39 +01:00
start = point ;
}
2016-11-15 16:33:27 +01:00
}
//---------------------------------------------------------
// slurDistance
//---------------------------------------------------------
2016-11-05 19:46:39 +01:00
2016-11-15 16:33:27 +01:00
inline static qreal slurDistance ( const Shape & shape , const QPointF & pt , qreal sdist , bool up )
{
qreal ddy ;
if ( up ) {
ddy = - shape . bottomDistance ( pt ) + sdist ;
if ( ddy < = 0.0 ) // assume no more collisions
2017-06-24 14:32:02 +02:00
ddy = 0.0 ;
2016-11-15 16:33:27 +01:00
}
else {
ddy = shape . topDistance ( pt ) - sdist ;
if ( ddy > = 0.0 )
ddy = 0.0 ;
}
return ddy ;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
2017-05-23 17:23:28 +02:00
// layoutSegment
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2015-05-05 18:28:39 +02:00
void SlurSegment : : layoutSegment ( const QPointF & p1 , const QPointF & p2 )
2012-05-26 14:26:10 +02:00
{
2018-12-06 13:55:15 +01:00
setPos ( QPointF ( ) ) ;
2018-03-07 10:46:57 +01:00
ups ( Grip : : START ) . p = p1 ;
ups ( Grip : : END ) . p = p2 ;
computeBezier ( ) ;
if ( MScore : : autoplaceSlurs & & autoplace ( ) & & system ( ) ) {
bool up = slur ( ) - > up ( ) ;
qreal gdist = 0.0 ;
Segment * ls = system ( ) - > lastMeasure ( ) - > last ( ) ;
Segment * fs = system ( ) - > firstMeasure ( ) - > first ( ) ;
2018-12-10 23:04:04 +01:00
Segment * ss = slur ( ) - > startSegment ( ) ;
Segment * es = slur ( ) - > endSegment ( ) ;
2018-03-07 10:46:57 +01:00
QPointF pp1 = ups ( Grip : : START ) . p ;
QPointF pp2 = ups ( Grip : : END ) . p ;
2018-11-21 14:34:20 +01:00
bool intersection = false ;
2018-03-07 10:46:57 +01:00
for ( Segment * s = fs ; s & & s ! = ls ; s = s - > next1 ( ) ) {
if ( ! s - > enabled ( ) )
continue ;
2018-12-18 17:19:05 +01:00
// skip start and end segments on assumption start and end points were placed well already
// this avoids overcorrection on collision with own ledger lines and accidentals
// it also avoids issues where slur appears to be attached to a note in a different voice
if ( s = = ss | | s = = es )
continue ;
// allow slurs to cross barlines
if ( s - > segmentType ( ) & SegmentType : : BarLineType )
continue ;
2018-03-07 10:46:57 +01:00
qreal x1 = s - > x ( ) + s - > measure ( ) - > x ( ) ;
qreal x2 = x1 + s - > width ( ) ;
if ( pp1 . x ( ) > x2 )
continue ;
if ( pp2 . x ( ) < x1 )
break ;
2018-11-21 14:34:20 +01:00
const Shape & segShape = s - > staffShape ( staffIdx ( ) ) . translated ( s - > pos ( ) + s - > measure ( ) - > pos ( ) ) ;
if ( ! intersection )
intersection = segShape . intersects ( _shape ) ;
2018-03-07 10:46:57 +01:00
if ( up ) {
//QPointF pt = QPointF(s->x() + s->measure()->x(), s->staffShape(staffIdx()).top() + s->y() + s->measure()->y());
2018-11-21 14:34:20 +01:00
qreal dist = _shape . minVerticalDistance ( segShape ) ;
2018-03-07 10:46:57 +01:00
if ( dist > 0.0 )
gdist = qMax ( gdist , dist ) ;
}
else {
//QPointF pt = QPointF(s->x() + s->measure()->x(), s->staffShape(staffIdx()).bottom() + s->y() + s->measure()->y());
2018-11-21 14:34:20 +01:00
qreal dist = segShape . minVerticalDistance ( _shape ) ;
2018-03-07 10:46:57 +01:00
if ( dist > 0.0 )
gdist = qMax ( gdist , dist ) ;
}
}
2018-11-21 14:34:20 +01:00
if ( intersection & & gdist > 0.0 ) {
2018-12-06 13:55:15 +01:00
qreal min = score ( ) - > styleP ( Sid : : SlurMinDistance ) + gdist ;
rypos ( ) + = up ? - min : min ;
2018-03-07 10:46:57 +01:00
}
}
setbbox ( path . boundingRect ( ) ) ;
}
2014-04-01 03:39:01 +02:00
//---------------------------------------------------------
// isEdited
//---------------------------------------------------------
bool SlurSegment : : isEdited ( ) const
{
2015-01-19 12:37:17 +01:00
for ( int i = 0 ; i < int ( Grip : : GRIPS ) ; + + i ) {
if ( ! _ups [ i ] . off . isNull ( ) )
2014-04-01 03:39:01 +02:00
return true ;
}
return false ;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// fixArticulations
//---------------------------------------------------------
2018-12-14 18:02:46 +01:00
static qreal fixArticulations ( qreal yo , Chord * c , qreal _up , bool stemSide = false )
2012-05-26 14:26:10 +02:00
{
//
2018-12-14 18:02:46 +01:00
// handle special case of tenuto and staccato
// yo = current offset of slur from chord position
// return unchanged position, or position of outmost "close" articulation
2012-05-26 14:26:10 +02:00
//
2018-01-29 15:03:49 +01:00
# if 1
2018-12-14 18:02:46 +01:00
for ( Articulation * a : c - > articulations ( ) ) {
if ( ! a - > layoutCloseToNote ( ) | | ! a - > autoplace ( ) | | ! a - > visible ( ) )
continue ;
// skip if articulation on stem side but slur is not or vice versa
if ( ( a - > up ( ) = = c - > up ( ) ) ! = stemSide )
continue ;
if ( a - > up ( ) )
yo = qMin ( yo , a - > y ( ) + ( a - > height ( ) + c - > score ( ) - > spatium ( ) * .3 ) * _up ) ;
else
yo = qMax ( yo , a - > y ( ) + ( a - > height ( ) + c - > score ( ) - > spatium ( ) * .3 ) * _up ) ;
}
return yo ;
# else
2016-02-06 22:03:43 +01:00
const QVector < Articulation * > & al = c - > articulations ( ) ;
2012-11-20 20:51:18 +01:00
if ( al . size ( ) > = 2 ) {
Articulation * a = al . at ( 1 ) ;
2018-12-14 18:02:46 +01:00
if ( a - > up ( ) = = c - > up ( ) & & ! stemSide )
2014-12-23 05:30:41 +01:00
return yo ;
2018-11-16 08:52:17 +01:00
else if ( a - > layoutCloseToNote ( ) )
2012-05-26 14:26:10 +02:00
return a - > y ( ) + ( a - > height ( ) + c - > score ( ) - > spatium ( ) * .3 ) * _up ;
}
2012-11-20 20:51:18 +01:00
else if ( al . size ( ) > = 1 ) {
Articulation * a = al . at ( 0 ) ;
2018-12-14 18:02:46 +01:00
if ( a - > up ( ) = = c - > up ( ) & & ! stemSide )
2014-12-23 05:30:41 +01:00
return yo ;
2018-11-16 08:52:17 +01:00
else if ( a - > layoutCloseToNote ( ) )
2012-05-26 14:26:10 +02:00
return a - > y ( ) + ( a - > height ( ) + c - > score ( ) - > spatium ( ) * .3 ) * _up ;
}
return yo ;
2018-12-14 18:02:46 +01:00
# endif
2012-05-26 14:26:10 +02:00
}
2013-06-16 23:33:37 +02:00
//---------------------------------------------------------
// slurPos
// Calculate position of start- and endpoint of slur
// relative to System() position.
//---------------------------------------------------------
void Slur : : slurPosChord ( SlurPos * sp )
{
2014-04-23 18:07:38 +02:00
Chord * stChord ;
Chord * enChord ;
2014-07-02 09:55:50 +02:00
if ( startChord ( ) - > isGraceAfter ( ) ) { // grace notes after, coming in reverse order
2014-04-23 18:07:38 +02:00
stChord = endChord ( ) ;
enChord = startChord ( ) ;
_up = false ;
}
2017-05-23 17:23:28 +02:00
else {
2014-04-23 18:07:38 +02:00
stChord = startChord ( ) ;
enChord = endChord ( ) ;
}
Note * _startNote = stChord - > downNote ( ) ;
Note * _endNote = enChord - > downNote ( ) ;
2018-04-28 15:12:29 +02:00
qreal hw = _startNote - > bboxRightPos ( ) ;
2013-06-16 23:33:37 +02:00
qreal __up = _up ? - 1.0 : 1.0 ;
qreal _spatium = spatium ( ) ;
2014-07-03 15:02:36 +02:00
Measure * measure = endChord ( ) - > measure ( ) ;
sp - > system1 = measure - > system ( ) ;
2017-05-23 17:23:28 +02:00
if ( ! sp - > system1 ) { // DEBUG
qDebug ( " no system1 " ) ;
2014-07-03 15:02:36 +02:00
return ;
2017-05-23 17:23:28 +02:00
}
2014-07-03 15:02:36 +02:00
Q_ASSERT ( sp - > system1 ) ;
2013-06-16 23:33:37 +02:00
sp - > system2 = sp - > system1 ;
QPointF pp ( sp - > system1 - > pagePos ( ) ) ;
qreal xo ;
qreal yo ;
//------p1
if ( _up ) {
xo = _startNote - > x ( ) + hw * 1.12 ;
yo = _startNote - > pos ( ) . y ( ) + hw * .3 * __up ;
}
else {
xo = _startNote - > x ( ) + hw * 0.4 ;
yo = _startNote - > pos ( ) . y ( ) + _spatium * .75 * __up ;
}
2014-04-23 18:07:38 +02:00
sp - > p1 = stChord - > pagePos ( ) - pp + QPointF ( xo , yo ) ;
2013-06-16 23:33:37 +02:00
//------p2
2014-04-23 18:07:38 +02:00
if ( ( enChord - > notes ( ) . size ( ) > 1 ) | | ( enChord - > stem ( ) & & ! enChord - > up ( ) & & ! _up ) ) {
2013-06-16 23:33:37 +02:00
xo = _endNote - > x ( ) - hw * 0.12 ;
yo = _endNote - > pos ( ) . y ( ) + hw * .3 * __up ;
}
else {
xo = _endNote - > x ( ) + hw * 0.15 ;
yo = _endNote - > pos ( ) . y ( ) + _spatium * .75 * __up ;
}
2014-04-23 18:07:38 +02:00
sp - > p2 = enChord - > pagePos ( ) - pp + QPointF ( xo , yo ) ;
2013-06-16 23:33:37 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// slurPos
// calculate position of start- and endpoint of slur
// relative to System() position
//---------------------------------------------------------
void Slur : : slurPos ( SlurPos * sp )
{
qreal _spatium = spatium ( ) ;
2013-06-16 23:33:37 +02:00
if ( endCR ( ) = = 0 ) {
sp - > p1 = startCR ( ) - > pagePos ( ) ;
sp - > p1 . rx ( ) + = startCR ( ) - > width ( ) ;
2012-05-26 14:26:10 +02:00
sp - > p2 = sp - > p1 ;
sp - > p2 . rx ( ) + = 5 * _spatium ;
2013-06-16 23:33:37 +02:00
sp - > system1 = startCR ( ) - > measure ( ) - > system ( ) ;
2012-05-26 14:26:10 +02:00
sp - > system2 = sp - > system1 ;
return ;
}
2016-12-13 13:16:17 +01:00
bool useTablature = staff ( ) & & staff ( ) - > isTabStaff ( endCR ( ) - > tick ( ) ) ;
2016-12-09 15:56:48 +01:00
bool staffHasStems = true ; // assume staff uses stems
2018-11-08 15:45:14 +01:00
const StaffType * stt = 0 ;
2015-09-06 10:34:42 +02:00
if ( useTablature ) {
2016-12-13 13:16:17 +01:00
stt = staff ( ) - > staffType ( tick ( ) ) ;
2016-12-09 15:56:48 +01:00
staffHasStems = stt - > stemThrough ( ) ; // if tab with stems beside, stems do not count for slur pos
2015-09-06 10:34:42 +02:00
}
2015-03-09 18:55:46 +01:00
2016-02-06 07:39:33 +01:00
// start and end cr, chord, and note
2014-07-08 11:16:21 +02:00
ChordRest * scr = startCR ( ) ;
ChordRest * ecr = endCR ( ) ;
Chord * sc = 0 ;
Note * note1 = 0 ;
2016-07-04 13:21:33 +02:00
if ( scr - > isChord ( ) ) {
sc = toChord ( scr ) ;
2012-09-17 12:17:54 +02:00
note1 = _up ? sc - > upNote ( ) : sc - > downNote ( ) ;
}
Chord * ec = 0 ;
Note * note2 = 0 ;
2016-07-04 13:21:33 +02:00
if ( ecr - > isChord ( ) ) {
ec = toChord ( ecr ) ;
2012-09-17 12:17:54 +02:00
note2 = _up ? ec - > upNote ( ) : ec - > downNote ( ) ;
2012-05-26 14:26:10 +02:00
}
2012-09-17 12:17:54 +02:00
sp - > system1 = scr - > measure ( ) - > system ( ) ;
sp - > system2 = ecr - > measure ( ) - > system ( ) ;
2016-12-09 15:56:48 +01:00
2017-05-23 17:23:28 +02:00
if ( sp - > system1 = = 0 ) {
qDebug ( " no system1 " ) ;
2013-10-14 15:34:46 +02:00
return ;
2017-05-23 17:23:28 +02:00
}
2013-10-14 15:34:46 +02:00
2017-07-05 17:41:47 +02:00
sp - > p1 = scr - > pos ( ) + scr - > segment ( ) - > pos ( ) + scr - > measure ( ) - > pos ( ) ;
sp - > p2 = ecr - > pos ( ) + ecr - > segment ( ) - > pos ( ) + ecr - > measure ( ) - > pos ( ) ;
2017-05-23 17:23:28 +02:00
2014-07-22 17:39:25 +02:00
// account for centering or other adjustments (other than mirroring)
2014-07-22 21:43:14 +02:00
if ( note1 & & ! note1 - > mirror ( ) )
2014-07-22 17:39:25 +02:00
sp - > p1 . rx ( ) + = note1 - > x ( ) ;
2014-07-22 21:43:14 +02:00
if ( note2 & & ! note2 - > mirror ( ) )
2014-07-22 17:39:25 +02:00
sp - > p2 . rx ( ) + = note2 - > x ( ) ;
2012-05-26 14:26:10 +02:00
qreal xo , yo ;
2015-09-06 10:34:42 +02:00
Stem * stem1 = sc & & staffHasStems ? sc - > stem ( ) : 0 ;
Stem * stem2 = ec & & staffHasStems ? ec - > stem ( ) : 0 ;
2012-05-26 14:26:10 +02:00
2014-05-26 12:54:37 +02:00
enum class SlurAnchor : char {
NONE , STEM
2012-07-24 18:21:22 +02:00
} ;
2014-05-26 12:54:37 +02:00
SlurAnchor sa1 = SlurAnchor : : NONE ;
SlurAnchor sa2 = SlurAnchor : : NONE ;
2016-02-06 07:39:33 +01:00
// if slur is 'embedded' between either stem or both (as it might happen with voices)
2015-08-24 00:12:38 +02:00
// link corresponding slur end to stem position
2012-09-17 12:17:54 +02:00
if ( ( scr - > up ( ) = = ecr - > up ( ) ) & & ! scr - > beam ( ) & & ! ecr - > beam ( ) & & ( _up = = scr - > up ( ) ) ) {
2016-02-06 07:39:33 +01:00
// both chords are facing same direction and slur is also in same direction
// and no beams
2012-08-10 09:21:29 +02:00
if ( stem1 )
2014-05-26 12:54:37 +02:00
sa1 = SlurAnchor : : STEM ;
2012-08-10 09:21:29 +02:00
if ( stem2 )
2014-05-26 12:54:37 +02:00
sa2 = SlurAnchor : : STEM ;
2012-07-24 18:21:22 +02:00
}
2018-12-10 23:04:04 +01:00
// also link start of slur to stem if start chord & slur are in same direction and there is a hook
if ( scr - > up ( ) = = _up & & stem1 & & sc - > hook ( ) ) {
sa1 = SlurAnchor : : STEM ;
// if end chord is in same direction, link end of slur to stem too
if ( ecr - > up ( ) = = scr - > up ( ) & & stem2 )
sa2 = SlurAnchor : : STEM ;
}
2012-07-24 18:21:22 +02:00
2012-08-10 09:21:29 +02:00
qreal __up = _up ? - 1.0 : 1.0 ;
2016-02-06 07:39:33 +01:00
qreal hw1 = note1 ? note1 - > tabHeadWidth ( stt ) : scr - > width ( ) ; // if stt == 0, tabHeadWidth()
qreal hw2 = note2 ? note2 - > tabHeadWidth ( stt ) : ecr - > width ( ) ; // defaults to headWidth()
2015-08-24 00:12:38 +02:00
QPointF pt ;
2012-08-10 09:21:29 +02:00
switch ( sa1 ) {
2015-08-24 00:12:38 +02:00
case SlurAnchor : : STEM : //sc can't be null
2018-12-10 23:04:04 +01:00
{
// place slur starting point at stem end point
2015-08-24 00:12:38 +02:00
pt = sc - > stemPos ( ) - sc - > pagePos ( ) + sc - > stem ( ) - > p2 ( ) ;
if ( useTablature ) // in tabs, stems are centred on note:
2018-04-28 15:12:29 +02:00
pt . rx ( ) = hw1 * 0.5 + ( note1 ? note1 - > bboxXShift ( ) : 0.0 ) ; // skip half notehead to touch stem, anatoly-os: incorrect. half notehead width is not always the stem position
2018-12-10 23:04:04 +01:00
// clear the stem (x)
// allow slight overlap (y) as per Gould
// don't allow overlap with hook if not disabling the autoplace checks against start/end segments in SlurSegment::layoutSegment()
qreal yadj = - 0.25 ; // sc->hook() ? 0.25 : -0.25;
yadj * = _spatium * __up ;
2018-12-14 18:02:46 +01:00
pt + = QPointF ( 0.35 * _spatium , yadj ) ;
// account for articulations
pt . ry ( ) = fixArticulations ( pt . y ( ) , sc , __up , true ) ;
sp - > p1 + = pt ;
2018-12-10 23:04:04 +01:00
}
2012-08-10 09:21:29 +02:00
break ;
2014-05-26 12:54:37 +02:00
case SlurAnchor : : NONE :
2012-08-10 09:21:29 +02:00
break ;
}
2016-02-06 07:39:33 +01:00
switch ( sa2 ) {
2015-08-24 00:12:38 +02:00
case SlurAnchor : : STEM : //ec can't be null
2018-12-10 23:04:04 +01:00
{
2015-08-24 00:12:38 +02:00
pt = ec - > stemPos ( ) - ec - > pagePos ( ) + ec - > stem ( ) - > p2 ( ) ;
if ( useTablature )
pt . rx ( ) = hw2 * 0.5 ;
2018-12-10 23:04:04 +01:00
// don't allow overlap with beam
qreal yadj = ec - > beam ( ) ? 0.75 : - 0.25 ;
yadj * = _spatium * __up ;
2018-12-14 18:02:46 +01:00
pt + = QPointF ( - 0.35 * _spatium , yadj ) ;
// account for articulations
pt . ry ( ) = fixArticulations ( pt . y ( ) , ec , __up , true ) ;
sp - > p2 + = pt ;
2018-12-10 23:04:04 +01:00
}
2012-08-10 09:21:29 +02:00
break ;
2014-05-26 12:54:37 +02:00
case SlurAnchor : : NONE :
2012-08-10 09:21:29 +02:00
break ;
2012-07-24 18:21:22 +02:00
}
2012-05-26 14:26:10 +02:00
//
// default position:
2016-05-03 01:21:42 +02:00
// horizontal: middle of notehead
// vertical: _spatium * .4 above/below notehead
2012-05-26 14:26:10 +02:00
//
//------p1
2015-08-26 15:35:40 +02:00
// Compute x0, y0 and stemPos
if ( sa1 = = SlurAnchor : : NONE | | sa2 = = SlurAnchor : : NONE ) { // need stemPos if sa2 == SlurAnchor::NONE
bool stemPos = false ; // p1 starts at chord stem side
2016-02-06 07:39:33 +01:00
// default positions
2018-04-28 15:12:29 +02:00
xo = hw1 * .5 + ( note1 ? note1 - > bboxXShift ( ) : 0.0 ) ;
2015-08-26 15:35:40 +02:00
if ( note1 )
yo = note1 - > pos ( ) . y ( ) ;
2016-02-06 07:39:33 +01:00
else if ( _up )
yo = scr - > bbox ( ) . top ( ) ;
2015-08-26 15:35:40 +02:00
else
2016-02-06 07:39:33 +01:00
yo = scr - > bbox ( ) . top ( ) + scr - > height ( ) ;
2015-08-26 15:35:40 +02:00
yo + = _spatium * .9 * __up ;
2016-02-06 07:39:33 +01:00
// adjustments for stem and/or beam
2015-08-26 15:35:40 +02:00
if ( stem1 ) { //sc not null
Beam * beam1 = sc - > beam ( ) ;
if ( beam1 & & ( beam1 - > elements ( ) . back ( ) ! = sc ) & & ( sc - > up ( ) = = _up ) ) {
2016-02-06 07:39:33 +01:00
// start chord is beamed but not the last chord of beam group
// and slur direction is same as start chord (stem side)
// in these cases, layout start of slur to stem
2015-08-26 15:35:40 +02:00
qreal sh = stem1 - > height ( ) + _spatium ;
if ( _up )
yo = sc - > downNote ( ) - > pos ( ) . y ( ) - sh ;
else
yo = sc - > upNote ( ) - > pos ( ) . y ( ) + sh ;
xo = stem1 - > pos ( ) . x ( ) ;
2016-02-06 07:39:33 +01:00
2018-12-14 18:02:46 +01:00
// account for articulations
yo = fixArticulations ( yo , sc , __up , true ) ;
2016-02-06 07:39:33 +01:00
// force end of slur to layout to stem as well,
// if start and end chords have same stem direction
stemPos = true ;
2015-08-26 15:35:40 +02:00
}
else {
2016-02-06 07:39:33 +01:00
// start chord is not beamed or is last chord of beam group
// or slur direction is opposite that of start chord
// at this point slur is in default position relative to note on slur side
// but we may need to make further adjustments
// if stem and slur are both up
// we need to clear stem horizontally
2015-08-26 15:35:40 +02:00
if ( sc - > up ( ) & & _up )
xo = hw1 + _spatium * .3 ;
2016-02-06 07:39:33 +01:00
2015-08-26 15:35:40 +02:00
//
// handle case: stem up - stem down
// stem down - stem up
//
if ( ( sc - > up ( ) ! = ecr - > up ( ) ) & & ( sc - > up ( ) = = _up ) ) {
2016-02-06 07:39:33 +01:00
// start and end chord have opposite direction
// and slur direction is same as start chord
// (so slur starts on stem side)
// float the start point along the stem to follow direction of movement
// see for example Gould p. 111
// get position of note on slur side for start & end chords
Note * n1 = sc - > up ( ) ? sc - > upNote ( ) : sc - > downNote ( ) ;
2015-08-26 15:35:40 +02:00
Note * n2 = 0 ;
2016-02-06 07:39:33 +01:00
if ( ec )
n2 = ec - > up ( ) ? ec - > upNote ( ) : ec - > downNote ( ) ;
2015-08-26 15:35:40 +02:00
2016-02-06 07:39:33 +01:00
// differential in note positions
qreal yd = ( n2 ? n2 - > pos ( ) . y ( ) : ecr - > pos ( ) . y ( ) ) - n1 - > pos ( ) . y ( ) ;
2015-08-26 15:35:40 +02:00
yd * = .5 ;
2016-02-06 07:39:33 +01:00
// float along stem according to differential
qreal sh = stem1 - > height ( ) ;
if ( _up & & yd < 0.0 )
yo = qMax ( yo + yd , sc - > downNote ( ) - > pos ( ) . y ( ) - sh - _spatium ) ;
else if ( ! _up & & yd > 0.0 )
yo = qMin ( yo + yd , sc - > upNote ( ) - > pos ( ) . y ( ) + sh + _spatium ) ;
2018-12-14 18:02:46 +01:00
// account for articulations
yo = fixArticulations ( yo , sc , __up , true ) ;
2016-02-06 07:39:33 +01:00
// we may wish to force end to align to stem as well,
// if it is in same direction
// (but it won't be, so this assignment should have no effect)
2015-08-26 15:35:40 +02:00
stemPos = true ;
2012-05-26 14:26:10 +02:00
}
2018-12-14 18:02:46 +01:00
else {
2016-02-06 07:39:33 +01:00
// avoid articulations
2018-12-14 18:02:46 +01:00
yo = fixArticulations ( yo , sc , __up , sc - > up ( ) = = _up ) ;
2016-02-06 07:39:33 +01:00
}
2012-05-26 14:26:10 +02:00
}
}
2018-12-14 18:02:46 +01:00
else if ( sc ) {
2016-02-06 07:39:33 +01:00
// avoid articulations
2018-12-14 18:02:46 +01:00
yo = fixArticulations ( yo , sc , __up , sc - > up ( ) = = _up ) ;
2016-02-06 07:39:33 +01:00
}
2015-08-26 15:35:40 +02:00
if ( sa1 = = SlurAnchor : : NONE )
sp - > p1 + = QPointF ( xo , yo ) ;
//------p2
if ( sa2 = = SlurAnchor : : NONE ) {
2016-02-06 07:39:33 +01:00
// default positions
2018-04-28 15:12:29 +02:00
xo = hw2 * .5 + ( note2 ? note2 - > bboxXShift ( ) : 0.0 ) ;
2015-08-26 15:35:40 +02:00
if ( note2 )
yo = note2 - > pos ( ) . y ( ) ;
2016-02-06 07:39:33 +01:00
else if ( _up )
2015-08-26 15:35:40 +02:00
yo = endCR ( ) - > bbox ( ) . top ( ) ;
2012-05-26 14:26:10 +02:00
else
2015-08-26 15:35:40 +02:00
yo = endCR ( ) - > bbox ( ) . top ( ) + endCR ( ) - > height ( ) ;
yo + = _spatium * .9 * __up ;
2016-02-06 07:39:33 +01:00
// adjustments for stem and/or beam
2015-08-26 15:35:40 +02:00
if ( stem2 ) { //ec can't be null
Beam * beam2 = ec - > beam ( ) ;
if ( ( stemPos & & ( scr - > up ( ) = = ec - > up ( ) ) )
| | ( beam2
2016-02-06 22:03:43 +01:00
& & ( ! beam2 - > elements ( ) . empty ( ) )
2015-08-26 15:35:40 +02:00
& & ( beam2 - > elements ( ) . front ( ) ! = ec )
& & ( ec - > up ( ) = = _up )
& & sc & & ( sc - > noteType ( ) = = NoteType : : NORMAL )
)
) {
2016-02-06 07:39:33 +01:00
// slur start was laid out to stem and start and end have same direction
// OR
// end chord is beamed but not the first chord of beam group
// and slur direction is same as end chord (stem side)
// and start chordrest is not a grace chord
// in these cases, layout end of slur to stem
2015-08-26 15:35:40 +02:00
qreal sh = stem2 - > height ( ) + _spatium ;
if ( _up )
yo = ec - > downNote ( ) - > pos ( ) . y ( ) - sh ;
else
yo = ec - > upNote ( ) - > pos ( ) . y ( ) + sh ;
xo = stem2 - > pos ( ) . x ( ) ;
2018-12-14 18:02:46 +01:00
// account for articulations
yo = fixArticulations ( yo , ec , __up , true ) ;
2015-08-26 15:35:40 +02:00
}
2016-02-06 07:39:33 +01:00
else
{
// slur was not aligned to stem or start and end have different direction
// AND
// end chord is not beamed or is first chord of beam group
// or slur direction is opposite that of end chord
// if stem and slur are both down,
// we need to clear stem horizontally
if ( ! ec - > up ( ) & & ! _up )
xo = - _spatium * .3 + note2 - > x ( ) ;
//
// handle case: stem up - stem down
// stem down - stem up
//
if ( ( scr - > up ( ) ! = ec - > up ( ) ) & & ( ec - > up ( ) = = _up ) ) {
// start and end chord have opposite direction
// and slur direction is same as end chord
// (so slur end on stem side)
// float the end point along the stem to follow direction of movement
// see for example Gould p. 111
Note * n1 = 0 ;
if ( sc )
n1 = sc - > up ( ) ? sc - > upNote ( ) : sc - > downNote ( ) ;
Note * n2 = ec - > up ( ) ? ec - > upNote ( ) : ec - > downNote ( ) ;
qreal yd = n2 - > pos ( ) . y ( ) - ( n1 ? n1 - > pos ( ) . y ( ) : startCR ( ) - > pos ( ) . y ( ) ) ;
yd * = .5 ;
qreal mh = stem2 - > height ( ) ;
if ( _up & & yd > 0.0 )
yo = qMax ( yo - yd , ec - > downNote ( ) - > pos ( ) . y ( ) - mh - _spatium ) ;
else if ( ! _up & & yd < 0.0 )
yo = qMin ( yo - yd , ec - > upNote ( ) - > pos ( ) . y ( ) + mh + _spatium ) ;
2018-12-14 18:02:46 +01:00
// account for articulations
yo = fixArticulations ( yo , ec , __up , true ) ;
2015-08-26 15:35:40 +02:00
}
2018-12-14 18:02:46 +01:00
else {
2016-02-06 07:39:33 +01:00
// avoid articulations
2018-12-14 18:02:46 +01:00
yo = fixArticulations ( yo , ec , __up , ec - > up ( ) = = _up ) ;
2015-08-26 15:35:40 +02:00
}
}
2012-05-26 14:26:10 +02:00
}
2018-12-14 18:02:46 +01:00
else if ( ec ) {
2016-02-06 07:39:33 +01:00
// avoid articulations
2018-12-14 18:02:46 +01:00
yo = fixArticulations ( yo , ec , __up , ec - > up ( ) = = _up ) ;
2016-02-06 07:39:33 +01:00
}
2012-05-26 14:26:10 +02:00
2015-08-26 15:35:40 +02:00
sp - > p2 + = QPointF ( xo , yo ) ;
2012-05-26 14:26:10 +02:00
}
}
}
//---------------------------------------------------------
// Slur
//---------------------------------------------------------
Slur : : Slur ( Score * s )
: SlurTie ( s )
{
2014-07-05 12:05:27 +02:00
setAnchor ( Anchor : : CHORD ) ;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// write
//---------------------------------------------------------
2016-11-19 11:51:21 +01:00
void Slur : : write ( XmlWriter & xml ) const
2012-05-26 14:26:10 +02:00
{
2016-10-26 11:33:51 +02:00
if ( broken ( ) ) {
2016-07-18 12:34:27 +02:00
qDebug ( " broken slur not written " ) ;
return ;
}
2014-08-15 17:20:20 +02:00
if ( ! xml . canWrite ( this ) )
return ;
2018-09-26 12:20:00 +02:00
xml . stag ( this ) ;
2012-05-26 14:26:10 +02:00
SlurTie : : writeProperties ( xml ) ;
xml . etag ( ) ;
}
//---------------------------------------------------------
// chordsHaveTie
//---------------------------------------------------------
static bool chordsHaveTie ( Chord * c1 , Chord * c2 )
{
2018-09-25 15:55:08 +02:00
size_t n = c1 - > notes ( ) . size ( ) ;
for ( size_t i1 = 0 ; i1 < n ; + + i1 ) {
2013-01-03 08:42:40 +01:00
Note * n1 = c1 - > notes ( ) . at ( i1 ) ;
2018-09-11 16:56:50 +02:00
size_t n2 = c2 - > notes ( ) . size ( ) ;
for ( size_t i2 = 0 ; i2 < n2 ; + + i2 ) {
2018-09-25 15:55:08 +02:00
Note * n3 = c2 - > notes ( ) . at ( i2 ) ;
if ( n1 - > tieFor ( ) & & n1 - > tieFor ( ) = = n3 - > tieBack ( ) )
2012-05-26 14:26:10 +02:00
return true ;
}
}
return false ;
}
//---------------------------------------------------------
// directionMixture
//---------------------------------------------------------
static bool isDirectionMixture ( Chord * c1 , Chord * c2 )
{
bool up = c1 - > up ( ) ;
2017-03-08 13:12:26 +01:00
for ( Segment * seg = c1 - > segment ( ) ; seg ; seg = seg - > next ( SegmentType : : ChordRest ) ) {
2016-07-05 14:23:10 +02:00
Element * e = seg - > element ( c1 - > track ( ) ) ;
if ( ! e | | ! e - > isChord ( ) )
2012-05-26 14:26:10 +02:00
continue ;
2016-07-05 14:23:10 +02:00
Chord * c = toChord ( e ) ;
2012-05-26 14:26:10 +02:00
if ( c - > up ( ) ! = up )
return true ;
if ( seg = = c2 - > segment ( ) )
break ;
}
return false ;
}
2016-07-01 12:42:15 +02:00
//---------------------------------------------------------
// layoutSystem
2016-12-09 15:56:48 +01:00
// layout slurSegment for system
2016-07-01 12:42:15 +02:00
//---------------------------------------------------------
2016-07-10 12:00:57 +02:00
SpannerSegment * Slur : : layoutSystem ( System * system )
2016-07-01 12:42:15 +02:00
{
int stick = system - > firstMeasure ( ) - > tick ( ) ;
int etick = system - > lastMeasure ( ) - > endTick ( ) ;
2018-11-25 23:35:00 +01:00
SlurSegment * slurSegment = toSlurSegment ( getNextLayoutSystemSegment ( system , [ this ] ( ) { return new SlurSegment ( score ( ) ) ; } ) ) ;
2016-07-01 12:42:15 +02:00
SpannerSegmentType sst ;
if ( tick ( ) > = stick ) {
//
// this is the first call to layoutSystem,
// processing the first line segment
//
if ( track2 ( ) = = - 1 )
setTrack2 ( track ( ) ) ;
if ( startCR ( ) = = 0 | | startCR ( ) - > measure ( ) = = 0 ) {
qDebug ( " Slur::layout(): track %d-%d %p - %p tick %d-%d null start anchor " ,
track ( ) , track2 ( ) , startCR ( ) , endCR ( ) , tick ( ) , tick2 ( ) ) ;
2016-07-10 12:00:57 +02:00
return slurSegment ;
2016-07-01 12:42:15 +02:00
}
if ( endCR ( ) = = 0 ) { // sanity check
setEndElement ( startCR ( ) ) ;
setTick2 ( tick ( ) ) ;
}
switch ( _slurDirection ) {
case Direction : : UP :
_up = true ;
break ;
case Direction : : DOWN :
_up = false ;
break ;
case Direction : : AUTO :
{
//
// assumption:
// slurs have only chords or rests as start/end elements
//
if ( startCR ( ) = = 0 | | endCR ( ) = = 0 ) {
_up = true ;
break ;
}
2016-07-01 16:52:13 +02:00
Chord * c1 = startCR ( ) - > isChord ( ) ? toChord ( startCR ( ) ) : 0 ;
Chord * c2 = endCR ( ) - > isChord ( ) ? toChord ( endCR ( ) ) : 0 ;
2016-07-01 12:42:15 +02:00
_up = ! ( startCR ( ) - > up ( ) ) ;
Measure * m1 = startCR ( ) - > measure ( ) ;
if ( ( endCR ( ) - > tick ( ) - startCR ( ) - > tick ( ) ) > m1 - > ticks ( ) ) // long slurs are always above
_up = true ;
else
_up = ! startCR ( ) - > up ( ) ;
if ( c1 & & c2 & & isDirectionMixture ( c1 , c2 ) & & ! c1 - > isGrace ( ) ) {
// slurs go above if start and end note have different stem directions,
// but grace notes are exceptions
_up = true ;
}
2016-12-12 12:02:18 +01:00
else if ( m1 - > hasVoices ( startCR ( ) - > staffIdx ( ) ) & & c1 & & ! c1 - > isGrace ( ) ) {
2016-07-01 12:42:15 +02:00
// in polyphonic passage, slurs go on the stem side
_up = startCR ( ) - > up ( ) ;
}
else if ( c1 & & c2 & & chordsHaveTie ( c1 , c2 ) ) {
// could confuse slur with tie, put slur on stem side
_up = startCR ( ) - > up ( ) ;
}
}
break ;
}
2016-12-09 15:56:48 +01:00
sst = tick2 ( ) < etick ? SpannerSegmentType : : SINGLE : SpannerSegmentType : : BEGIN ;
2016-07-01 12:42:15 +02:00
}
2018-12-07 17:57:31 +01:00
else if ( tick ( ) < stick & & tick2 ( ) > = etick )
2016-07-01 12:42:15 +02:00
sst = SpannerSegmentType : : MIDDLE ;
else
sst = SpannerSegmentType : : END ;
slurSegment - > setSpannerSegmentType ( sst ) ;
SlurPos sPos ;
slurPos ( & sPos ) ;
switch ( sst ) {
case SpannerSegmentType : : SINGLE :
slurSegment - > layoutSegment ( sPos . p1 , sPos . p2 ) ;
break ;
case SpannerSegmentType : : BEGIN :
slurSegment - > layoutSegment ( sPos . p1 , QPointF ( system - > bbox ( ) . width ( ) , sPos . p1 . y ( ) ) ) ;
break ;
case SpannerSegmentType : : MIDDLE : {
qreal x1 = firstNoteRestSegmentX ( system ) ;
qreal x2 = system - > bbox ( ) . width ( ) ;
qreal y = staffIdx ( ) > system - > staves ( ) - > size ( ) ? system - > y ( ) : system - > staff ( staffIdx ( ) ) - > y ( ) ;
slurSegment - > layoutSegment ( QPointF ( x1 , y ) , QPointF ( x2 , y ) ) ;
}
break ;
case SpannerSegmentType : : END :
slurSegment - > layoutSegment ( QPointF ( firstNoteRestSegmentX ( system ) , sPos . p2 . y ( ) ) , sPos . p2 ) ;
break ;
}
2016-07-10 12:00:57 +02:00
return slurSegment ;
2016-07-01 12:42:15 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Slur : : layout ( )
{
2013-09-27 20:37:40 +02:00
if ( track2 ( ) = = - 1 )
2013-10-03 08:05:32 +02:00
setTrack2 ( track ( ) ) ;
2013-09-27 20:37:40 +02:00
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium ( ) ;
2013-06-10 11:03:34 +02:00
if ( score ( ) = = gscore | | tick ( ) = = - 1 ) {
2012-05-26 14:26:10 +02:00
//
// when used in a palette, slur has no parent and
// tick and tick2 has no meaning so no layout is
// possible and needed
//
SlurSegment * s ;
2016-02-06 22:03:43 +01:00
if ( spannerSegments ( ) . empty ( ) ) {
2012-05-26 14:26:10 +02:00
s = new SlurSegment ( score ( ) ) ;
s - > setTrack ( track ( ) ) ;
add ( s ) ;
}
else {
s = frontSegment ( ) ;
}
2014-05-26 20:38:22 +02:00
s - > setSpannerSegmentType ( SpannerSegmentType : : SINGLE ) ;
2015-05-05 18:28:39 +02:00
s - > layoutSegment ( QPointF ( 0 , 0 ) , QPointF ( _spatium * 6 , 0 ) ) ;
2012-05-26 14:26:10 +02:00
setbbox ( frontSegment ( ) - > bbox ( ) ) ;
return ;
}
2013-06-20 18:48:28 +02:00
2015-05-12 18:22:48 +02:00
if ( startCR ( ) = = 0 | | startCR ( ) - > measure ( ) = = 0 ) {
2014-07-21 13:24:21 +02:00
qDebug ( " Slur::layout(): track %d-%d %p - %p tick %d-%d null start anchor " ,
track ( ) , track2 ( ) , startCR ( ) , endCR ( ) , tick ( ) , tick2 ( ) ) ;
2013-06-10 21:13:04 +02:00
return ;
}
2014-08-12 20:10:46 +02:00
if ( endCR ( ) = = 0 ) { // sanity check
2014-07-05 12:05:27 +02:00
setEndElement ( startCR ( ) ) ;
setTick2 ( tick ( ) ) ;
}
2012-05-26 14:26:10 +02:00
switch ( _slurDirection ) {
2016-03-02 13:20:19 +01:00
case Direction : : UP :
2012-05-26 14:26:10 +02:00
_up = true ;
break ;
2016-03-02 13:20:19 +01:00
case Direction : : DOWN :
2012-05-26 14:26:10 +02:00
_up = false ;
break ;
2016-03-02 13:20:19 +01:00
case Direction : : AUTO :
2012-05-26 14:26:10 +02:00
{
//
// assumption:
// slurs have only chords or rests as start/end elements
//
2013-06-16 23:33:37 +02:00
if ( startCR ( ) = = 0 | | endCR ( ) = = 0 ) {
2012-05-26 14:26:10 +02:00
_up = true ;
break ;
}
2014-07-05 12:05:27 +02:00
Measure * m1 = startCR ( ) - > measure ( ) ;
2012-05-26 14:26:10 +02:00
2016-07-04 13:21:33 +02:00
Chord * c1 = startCR ( ) - > isChord ( ) ? toChord ( startCR ( ) ) : 0 ;
Chord * c2 = endCR ( ) - > isChord ( ) ? toChord ( endCR ( ) ) : 0 ;
2012-05-26 14:26:10 +02:00
2013-06-16 23:33:37 +02:00
_up = ! ( startCR ( ) - > up ( ) ) ;
2012-05-26 14:26:10 +02:00
2013-06-16 23:33:37 +02:00
if ( ( endCR ( ) - > tick ( ) - startCR ( ) - > tick ( ) ) > m1 - > ticks ( ) ) {
2012-05-26 14:26:10 +02:00
// long slurs are always above
_up = true ;
}
else
2013-06-16 23:33:37 +02:00
_up = ! ( startCR ( ) - > up ( ) ) ;
2012-05-26 14:26:10 +02:00
2014-05-27 10:35:28 +02:00
if ( c1 & & c2 & & isDirectionMixture ( c1 , c2 ) & & ( c1 - > noteType ( ) = = NoteType : : NORMAL ) ) {
2012-05-26 14:26:10 +02:00
// slurs go above if start and end note have different stem directions,
// but grace notes are exceptions
_up = true ;
}
2016-12-12 12:02:18 +01:00
else if ( m1 - > hasVoices ( startCR ( ) - > staffIdx ( ) ) & & c1 & & c1 - > noteType ( ) = = NoteType : : NORMAL ) {
2012-05-26 14:26:10 +02:00
// in polyphonic passage, slurs go on the stem side
2013-06-16 23:33:37 +02:00
_up = startCR ( ) - > up ( ) ;
2012-05-26 14:26:10 +02:00
}
else if ( c1 & & c2 & & chordsHaveTie ( c1 , c2 ) ) {
// could confuse slur with tie, put slur on stem side
2013-06-16 23:33:37 +02:00
_up = startCR ( ) - > up ( ) ;
2012-05-26 14:26:10 +02:00
}
}
break ;
}
SlurPos sPos ;
slurPos ( & sPos ) ;
2016-02-04 11:27:47 +01:00
const QList < System * > & sl = score ( ) - > systems ( ) ;
ciSystem is = sl . begin ( ) ;
while ( is ! = sl . end ( ) ) {
2012-05-26 14:26:10 +02:00
if ( * is = = sPos . system1 )
break ;
+ + is ;
}
2016-02-04 11:27:47 +01:00
if ( is = = sl . end ( ) )
2012-05-26 14:26:10 +02:00
qDebug ( " Slur::layout first system not found " ) ;
setPos ( 0 , 0 ) ;
//---------------------------------------------------------
// count number of segments, if no change, all
// user offsets (drags) are retained
//---------------------------------------------------------
unsigned nsegs = 1 ;
2016-02-04 11:27:47 +01:00
for ( ciSystem iis = is ; iis ! = sl . end ( ) ; + + iis ) {
2016-03-02 13:20:19 +01:00
if ( ( * iis ) - > vbox ( ) )
2012-05-26 14:26:10 +02:00
continue ;
if ( * iis = = sPos . system2 )
break ;
+ + nsegs ;
}
fixupSegments ( nsegs ) ;
2016-02-04 11:27:47 +01:00
for ( int i = 0 ; is ! = sl . end ( ) ; + + i , + + is ) {
2012-05-26 14:26:10 +02:00
System * system = * is ;
2016-03-02 13:20:19 +01:00
if ( system - > vbox ( ) ) {
2012-05-26 14:26:10 +02:00
- - i ;
continue ;
}
SlurSegment * segment = segmentAt ( i ) ;
segment - > setSystem ( system ) ;
// case 1: one segment
if ( sPos . system1 = = sPos . system2 ) {
2014-05-26 20:38:22 +02:00
segment - > setSpannerSegmentType ( SpannerSegmentType : : SINGLE ) ;
2015-05-05 18:28:39 +02:00
segment - > layoutSegment ( sPos . p1 , sPos . p2 ) ;
2012-05-26 14:26:10 +02:00
}
// case 2: start segment
else if ( i = = 0 ) {
2014-05-26 20:38:22 +02:00
segment - > setSpannerSegmentType ( SpannerSegmentType : : BEGIN ) ;
2012-05-26 14:26:10 +02:00
qreal x = system - > bbox ( ) . width ( ) ;
2015-05-05 18:28:39 +02:00
segment - > layoutSegment ( sPos . p1 , QPointF ( x , sPos . p1 . y ( ) ) ) ;
2012-05-26 14:26:10 +02:00
}
// case 3: middle segment
else if ( i ! = 0 & & system ! = sPos . system2 ) {
2014-05-26 20:38:22 +02:00
segment - > setSpannerSegmentType ( SpannerSegmentType : : MIDDLE ) ;
2014-10-31 23:41:31 +01:00
qreal x1 = firstNoteRestSegmentX ( system ) ;
2012-05-26 14:26:10 +02:00
qreal x2 = system - > bbox ( ) . width ( ) ;
2016-06-20 15:33:45 +02:00
qreal y = staffIdx ( ) > system - > staves ( ) - > size ( ) ? system - > y ( ) : system - > staff ( staffIdx ( ) ) - > y ( ) ;
2015-05-05 18:28:39 +02:00
segment - > layoutSegment ( QPointF ( x1 , y ) , QPointF ( x2 , y ) ) ;
2012-05-26 14:26:10 +02:00
}
// case 4: end segment
else {
2014-05-26 20:38:22 +02:00
segment - > setSpannerSegmentType ( SpannerSegmentType : : END ) ;
2014-10-31 23:41:31 +01:00
qreal x = firstNoteRestSegmentX ( system ) ;
2015-05-05 18:28:39 +02:00
segment - > layoutSegment ( QPointF ( x , sPos . p2 . y ( ) ) , sPos . p2 ) ;
2012-05-26 14:26:10 +02:00
}
if ( system = = sPos . system2 )
break ;
}
2016-02-06 22:03:43 +01:00
setbbox ( spannerSegments ( ) . empty ( ) ? QRectF ( ) : frontSegment ( ) - > bbox ( ) ) ;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Slur : : setTrack ( int n )
{
Element : : setTrack ( n ) ;
2016-07-04 13:21:33 +02:00
for ( SpannerSegment * ss : spannerSegments ( ) )
2012-05-26 14:26:10 +02:00
ss - > setTrack ( n ) ;
}
2013-05-13 18:49:17 +02:00
}