MuseScore/libmscore/beam.cpp

2231 lines
79 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
// $Id: beam.cpp 5656 2012-05-21 15:36:47Z wschweer $
//
// Copyright (C) 2002-2012 Werner Schweer
//
// 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 "beam.h"
#include "segment.h"
#include "score.h"
#include "chord.h"
#include "sig.h"
#include "style.h"
#include "note.h"
#include "tuplet.h"
#include "system.h"
#include "tremolo.h"
#include "measure.h"
#include "undo.h"
#include "staff.h"
#include "stafftype.h"
#include "stem.h"
#include "hook.h"
#include "mscore.h"
#include "icon.h"
//---------------------------------------------------------
// BeamFragment
// position of primary beam
2012-08-04 15:46:43 +02:00
// idx 0 - MScore::AUTO or MScore::DOWN
// 1 - MScore::UP
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
struct BeamFragment {
qreal py1[2];
qreal py2[2];
};
//---------------------------------------------------------
// BeamHint
// beam hint for autobeamer
//---------------------------------------------------------
struct BeamHint {
Fraction noteLen;
Fraction prevNoteLen; // zero = all notes
Fraction timeSig; // valid for this timesig; zero = valid for all
Fraction pos;
BeamHint(Fraction sig, Fraction p, Fraction len, Fraction prevLen)
: noteLen(len), prevNoteLen(prevLen), timeSig(sig), pos(p) {}
};
//---------------------------------------------------------
// endBeam
//---------------------------------------------------------
static BeamHint endBeamList[] = {
// in 2 2 time
// end beams each 1 2 note
BeamHint(Fraction(2,2), Fraction(1,2), Fraction(0,0), Fraction(0,0)),
// in 3 2 time:
// end beams each 1 2 note
// end beams with 16th notes each 1 4 note
// end beams with 32th notes each 1 8 note
// noteLen timesig position
BeamHint(Fraction(3,2), Fraction(1,2), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(2,2), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,2), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(3,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,1), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(5,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(3,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,2), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(5,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(3,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(7,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(1,1), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(9,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(5,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,2), Fraction(11,8),Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(2,4), Fraction(0,0), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(2,4), Fraction(1,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(2,4), Fraction(1,9), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(2,4), Fraction(3,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,2), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,2), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(3,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(1,2), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(3,4), Fraction(5,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(12,16), Fraction(3,8), Fraction(0, 0), Fraction(0,0)),
BeamHint(Fraction(12,16), Fraction(3,16), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(12,16), Fraction(6,16), Fraction(1,8), Fraction(0,0)),
BeamHint(Fraction(12,16), Fraction(9,16), Fraction(1,8), Fraction(0,0)),
BeamHint(Fraction(12,16), Fraction(9,16), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(1,2), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(1,4), Fraction(1,12), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(3,4), Fraction(1,12), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(1,4), Fraction(1,8), Fraction(1,16)), // ws
BeamHint(Fraction(4,4), Fraction(2,4), Fraction(1,8), Fraction(1,16)), // ws
BeamHint(Fraction(4,4), Fraction(3,4), Fraction(1,8), Fraction(1,16)), // ws
BeamHint(Fraction(4,4), Fraction(1,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(3,4), Fraction(1,16), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(1,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(1,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(3,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(5,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(3,4), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,4), Fraction(7,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(5,4), Fraction(3,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(6,4), Fraction(3,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(3,8), Fraction(3,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,8), Fraction(0,0), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,8), Fraction(1,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,8), Fraction(1,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(4,8), Fraction(3,8), Fraction(1,32), Fraction(0,0)),
BeamHint(Fraction(6,8), Fraction(3,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(9,8), Fraction(3,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(9,8), Fraction(3,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(12,8), Fraction(3,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(12,8), Fraction(3,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(12,8), Fraction(9,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(15,8), Fraction(3,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(15,8), Fraction(3,4), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(15,8), Fraction(9,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(15,8), Fraction(6,8), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,16), Fraction(0,0), Fraction(0,0), Fraction(0,0)),
BeamHint(Fraction(4,16), Fraction(1,8), Fraction(0,0), Fraction(0,0))
};
//---------------------------------------------------------
// endBeam
// return true if beam should be ended
//---------------------------------------------------------
bool endBeam(const Fraction& ts, ChordRest* cr, ChordRest* prevCr)
{
int p = cr->tick() - cr->measure()->tick();
if (cr->tuplet() && !cr->tuplet()->elements().isEmpty()) {
if (cr->tuplet()->elements().front() == cr) // end beam at tuplet
return true;
return false;
}
Fraction l = cr->duration();
Fraction pl = prevCr ? prevCr->duration() : Fraction(0,1);
for (unsigned i = 0; i < sizeof(endBeamList)/sizeof(*endBeamList); ++i) {
const BeamHint& h = endBeamList[i];
if (!h.timeSig.isZero() && (!h.timeSig.identical(ts)))
continue;
if (!h.noteLen.isZero() && (h.noteLen != l))
continue;
if (!h.prevNoteLen.isZero() && (h.prevNoteLen != pl))
continue;
if (!h.pos.isZero()) {
int pos = h.pos.ticks();
if (pos != p)
continue;
}
else { // if (h.pos.numerator() == 0) { // stop on every beat
int len = (4 * MScore::division) / h.timeSig.denominator();
if (p % len) {
continue;
}
}
return true;
}
return false;
}
//---------------------------------------------------------
// Beam
//---------------------------------------------------------
Beam::Beam(Score* s)
: Element(s)
{
setFlags(ELEMENT_SELECTABLE);
2012-08-04 15:46:43 +02:00
_direction = MScore::AUTO;
2012-05-26 14:26:10 +02:00
_up = true;
_distribute = false;
_userModified[0] = false;
_userModified[1] = false;
_grow1 = 1.0;
_grow2 = 1.0;
editFragment = 0;
}
//---------------------------------------------------------
// Beam
//---------------------------------------------------------
Beam::Beam(const Beam& b)
: Element(b)
{
_elements = b._elements;
foreach(QLineF* bs, b.beamSegments)
beamSegments.append(new QLineF(*bs));
_direction = b._direction;
_up = b._up;
2012-09-24 16:55:53 +02:00
_distribute = b._distribute;
2012-05-26 14:26:10 +02:00
_userModified[0] = b._userModified[0];
_userModified[1] = b._userModified[1];
_grow1 = b._grow1;
_grow2 = b._grow2;
foreach(BeamFragment* f, b.fragments)
fragments.append(new BeamFragment(*f));
minMove = b.minMove;
maxMove = b.maxMove;
isGrace = b.isGrace;
cross = b.cross;
maxDuration = b.maxDuration;
slope = b.slope;
}
//---------------------------------------------------------
// Beam
//---------------------------------------------------------
Beam::~Beam()
{
//
// delete all references from chords
//
foreach(ChordRest* cr, _elements)
cr->setBeam(0);
qDeleteAll(beamSegments);
qDeleteAll(fragments);
}
//---------------------------------------------------------
// pagePos
//---------------------------------------------------------
QPointF Beam::pagePos() const
{
System* system = static_cast<System*>(parent());
if (system == 0)
return pos();
qreal yp = y() + system->staff(staffIdx())->y() + system->y();
return QPointF(pageX(), yp);
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Beam::add(ChordRest* a)
{
a->setBeam(this);
if (!_elements.contains(a)) {
//
// insert element in same order as it appears
// in the score
//
if (a->segment() && !_elements.isEmpty()) {
for (int i = 0; i < _elements.size(); ++i) {
Segment* s = _elements[i]->segment();
if ((s->tick() > a->segment()->tick())
|| ((s->tick() == a->segment()->tick()) && (a->segment()->next(Segment::SegChordRest) == s))
2012-05-26 14:26:10 +02:00
) {
_elements.insert(i, a);
return;
}
}
}
_elements.append(a);
}
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Beam::remove(ChordRest* a)
{
if (!_elements.removeOne(a))
qDebug("Beam::remove(): cannot find ChordRest");
a->setBeam(0);
}
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Beam::draw(QPainter* painter) const
{
if (staff()->isTabStaff()) {
2012-05-26 14:26:10 +02:00
if (staff()->staffType()->slashStyle())
return;
}
painter->setBrush(QBrush(curColor()));
painter->setPen(Qt::NoPen);
qreal lw2 = point(score()->styleS(ST_beamWidth)) * .5 * mag();
foreach (const QLineF* bs, beamSegments) {
QPolygonF pg;
pg << QPointF(bs->x1(), bs->y1()-lw2)
<< QPointF(bs->x2(), bs->y2()-lw2)
<< QPointF(bs->x2(), bs->y2()+lw2)
<< QPointF(bs->x1(), bs->y1()+lw2);
painter->drawPolygon(pg, Qt::OddEvenFill);
}
}
//---------------------------------------------------------
// move
//---------------------------------------------------------
void Beam::move(qreal x, qreal y)
{
Element::move(x, y);
foreach (QLineF* bs, beamSegments)
bs->translate(x, y);
}
//---------------------------------------------------------
// twoBeamedNotes
// calculate stem direction of two beamed notes
// return true if two beamed notes found
//---------------------------------------------------------
bool Beam::twoBeamedNotes()
{
// if not two elements or elements are not chords or chords have more than 1 note, return failure
2012-05-26 14:26:10 +02:00
if ((_elements.size() != 2)
|| (_elements[0]->type() != CHORD)
|| _elements[1]->type() != CHORD) {
return false;
}
const Chord* c1 = static_cast<const Chord*>(_elements[0]);
const Chord* c2 = static_cast<const Chord*>(_elements[1]);
if (c1->notes().size() != 1 || c2->notes().size() != 1)
return false;
int upDnLimit = staff()->lines() - 1; // was '4' hard-coded in the next 2 lines
int dist1 = c1->upLine() - upDnLimit;
int dist2 = c2->upLine() - upDnLimit;
2012-05-26 14:26:10 +02:00
if ((dist1 == -dist2) || (-dist1 == dist2)) {
_up = false;
Segment* s = c1->segment();
s = s->prev1(Segment::SegChordRest);
2012-05-26 14:26:10 +02:00
if (s && s->element(c1->track())) {
Chord* c = static_cast<Chord*>(s->element(c1->track()));
if ((c->type() == CHORD) && c->beam())
_up = c->beam()->up();
}
}
else if (qAbs(dist1) > qAbs(dist2))
_up = dist1 > 0;
else
_up = dist2 > 0;
return true;
}
//---------------------------------------------------------
// layout1
//---------------------------------------------------------
void Beam::layout1()
{
//delete old segments
2012-08-23 12:28:27 +02:00
qDeleteAll(beamSegments);
2012-05-26 14:26:10 +02:00
beamSegments.clear();
maxDuration.setType(TDuration::V_INVALID);
Chord* c1 = 0;
Chord* c2 = 0;
// TAB's with stem beside staves have special layout
if (staff()->isTabStaff() && !((StaffTypeTablature*)staff()->staffType())->stemThrough()) {
//TABULATURES: all beams (and related chords) are:
// UP or DOWN according to TAB duration position
// slope 0
_up = !((StaffTypeTablature*)staff()->staffType())->stemsDown();
2012-05-26 14:26:10 +02:00
slope = 0.0;
cross = isGrace = false;
minMove = maxMove = 0; // no cross-beaming in TAB's!
2012-05-26 14:26:10 +02:00
foreach(ChordRest* cr, _elements) {
if (cr->type() == CHORD) {
// set members maxDuration, c1, c2
if (!maxDuration.isValid() || (maxDuration < cr->durationType()))
maxDuration = cr->durationType();
c2 = static_cast<Chord*>(cr);
if (c2->noteType() != NOTE_NORMAL)
isGrace = true;
if (c1 == 0)
c1 = c2;
}
}
}
else {
//PITCHED STAVES (and TAB's with stems through staves)
2012-05-26 14:26:10 +02:00
minMove = 1000;
maxMove = -1000;
isGrace = false;
int upCount = 0;
int mUp = 0;
int mDown = 0;
int upDnLimit = staff()->lines() - 1; // was '4' hard-coded in following code
2012-05-26 14:26:10 +02:00
foreach(ChordRest* cr, _elements) {
if (cr->type() == CHORD) {
c2 = static_cast<Chord*>(cr);
if (c2->line() != upDnLimit)
2012-05-26 14:26:10 +02:00
upCount += c2->up() ? 1 : -1;
if (c2->noteType() != NOTE_NORMAL)
isGrace = true;
if (c1 == 0)
c1 = c2;
int i = c2->staffMove();
if (i < minMove)
minMove = i;
if (i > maxMove)
maxMove = i;
int line = c2->upLine();
if ((line - upDnLimit) > mUp)
mUp = line - upDnLimit;
line = c2->downLine();
if (upDnLimit - line > mDown)
mDown = upDnLimit - line;
2012-05-26 14:26:10 +02:00
}
if (!maxDuration.isValid() || (maxDuration < cr->durationType()))
maxDuration = cr->durationType();
}
//
// determine beam stem direction
//
2012-08-04 15:46:43 +02:00
if (_direction != MScore::AUTO) {
_up = _direction == MScore::UP;
2012-05-26 14:26:10 +02:00
}
else {
ChordRest* cr = _elements[0];
NoteType noteType = NOTE_NORMAL;
if (cr->type() == CHORD)
noteType = static_cast<Chord*>(cr)->noteType();
else {
for (int i = 1; i < _elements.size(); ++i) {
if (_elements[i]->type() == CHORD) {
noteType = static_cast<Chord*>(_elements[i])->noteType();
break;
}
}
}
2012-05-26 14:26:10 +02:00
Measure* m = cr->measure();
if (m->hasVoices(cr->staffIdx())) {
switch(cr->voice()) {
2012-08-04 15:46:43 +02:00
case 0: _up = (score()->style(ST_stemDir1).toDirection() == MScore::UP); break;
case 1: _up = (score()->style(ST_stemDir2).toDirection() == MScore::UP); break;
case 2: _up = (score()->style(ST_stemDir3).toDirection() == MScore::UP); break;
case 3: _up = (score()->style(ST_stemDir4).toDirection() == MScore::UP); break;
2012-05-26 14:26:10 +02:00
}
}
else if (noteType != NOTE_NORMAL)
_up = true;
2012-05-26 14:26:10 +02:00
else if (!twoBeamedNotes()) {
// highest or lowest note determines stem direction
// down-stems is preferred if equal
_up = mUp > mDown;
2012-05-26 14:26:10 +02:00
}
}
cross = minMove < maxMove;
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
slope = 0.0;
if (cross || _userModified[idx]) {
//
// guess stem direction for every chord
//
#if 0
2012-05-26 14:26:10 +02:00
foreach(ChordRest* cr, _elements) {
if (cr->type() != CHORD)
continue;
Chord* c = static_cast<Chord*>(cr);
int move = c->staffMove();
if (move == 0)
c->setUp(maxMove ? false : true);
else if (move > 0)
c->setUp(true);
else if (move < 0)
c->setUp(false);
}
#endif
2012-05-26 14:26:10 +02:00
}
else {
foreach(ChordRest* cr, _elements)
cr->setUp(_up);
}
} // end of if/else(tablature)
}
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Beam::layout()
{
System* system = _elements.front()->measure()->system();
setParent(system);
QList<ChordRest*> crl;
int n = 0;
foreach(ChordRest* cr, _elements) {
if (cr->measure()->system() != system) {
SpannerSegmentType st;
if (n == 0)
st = SEGMENT_BEGIN;
else
st = SEGMENT_MIDDLE;
++n;
if (fragments.size() < n)
fragments.append(new BeamFragment);
layout2(crl, st, n-1);
crl.clear();
system = cr->measure()->system();
}
crl.append(cr);
}
if (!crl.isEmpty()) {
SpannerSegmentType st;
if (n == 0)
st = SEGMENT_SINGLE;
else
st = SEGMENT_END;
if (fragments.size() < (n+1))
fragments.append(new BeamFragment);
layout2(crl, st, n);
}
setbbox(QRectF());
qreal lw2 = point(score()->styleS(ST_beamWidth)) * .5 * mag();
foreach(const QLineF* bs, beamSegments) {
QPolygonF a(4);
a[0] = QPointF(bs->x1(), bs->y1()-lw2);
a[1] = QPointF(bs->x2(), bs->y2()-lw2);
a[2] = QPointF(bs->x2(), bs->y2()+lw2);
a[3] = QPointF(bs->x1(), bs->y1()+lw2);
addbbox(a.boundingRect());
}
}
//---------------------------------------------------------
// shape
//---------------------------------------------------------
QPainterPath Beam::shape() const
{
QPainterPath pp;
qreal lw2 = point(score()->styleS(ST_beamWidth)) * .5 * mag();
foreach(const QLineF* bs, beamSegments) {
QPolygonF a(5);
a[0] = QPointF(bs->x1(), bs->y1()-lw2);
a[1] = QPointF(bs->x2(), bs->y2()-lw2);
a[2] = QPointF(bs->x2(), bs->y2()+lw2);
a[3] = QPointF(bs->x1(), bs->y1()+lw2);
a[4] = QPointF(bs->x1(), bs->y1()-lw2);
pp.addPolygon(a);
}
return pp;
}
//---------------------------------------------------------
// contains
//---------------------------------------------------------
bool Beam::contains(const QPointF& p) const
{
return shape().contains(p - pagePos());
}
//---------------------------------------------------------
// absLimit
//---------------------------------------------------------
inline qreal absLimit(qreal val, qreal limit)
{
if (val > limit)
return limit;
if (val < -limit)
return -limit;
return val;
}
//---------------------------------------------------------
// noSlope
//---------------------------------------------------------
2012-09-25 13:33:50 +02:00
bool Beam::noSlope(const QList<ChordRest*>& cl)
2012-05-26 14:26:10 +02:00
{
if (cl.size() < 2)
return true;
2012-09-25 13:33:50 +02:00
//
// return true if beam spans a rest
//
foreach(const ChordRest* cr, cl) {
if (cr->type() != CHORD)
return true;
}
2012-05-26 14:26:10 +02:00
int l1 = cl.front()->line();
int le = cl.back()->line();
// look for some pattern
if (cl.size() == 4) {
int l2 = cl[1]->line();
int l3 = cl[2]->line();
if ((l1 < le) && (l2 > l1) && (l2 > l3) && (l3 > le)) {
return true;
}
if ((l1 == l3) && (l2 == le))
return true;
}
else if (cl.size() == 6) {
int l2 = cl[1]->line();
int l3 = cl[2]->line();
int l4 = cl[3]->line();
int l5 = cl[4]->line();
if ((l2 > l1) && (l3 > l2) && (l1 == l4) && (l2 == l5) && (l3 == le))
return true;
}
//
// concave beams have a slope of 0.0
//
bool sameLine = true;
slope = 0.0;
if (cl.size() >= 3) {
int l4 = cl[1]->line(_up);
for (int i = 1; i < cl.size()-1; ++i) {
int l3 = cl[i]->line(_up);
if (l3 != l4)
sameLine = false;
if (_up) {
if (l3 < l1 && l3 < le)
return true;
}
else {
if (l3 > l1 && l3 > le)
return true;
}
}
if (sameLine && (l1 == l4 || le == l4)) {
if (_up) {
if (l1 == l4 && l1 < le)
return true;
if (le == l4 && le < l1)
return true;
}
else {
if (l1 == l4 && l1 > le)
return true;
else if (le == l4 && le > l1)
return true;
}
}
}
return l1 == le;
}
//---------------------------------------------------------
// BeamMetric
//---------------------------------------------------------
struct Bm
{
char l; // stem len in 1/4 spatium units
char s; // beam slant in 1/4 spatium units
Bm() : l(0), s(0) {}
Bm(char a, char b) : l(a), s(b) {}
static int key(int a, int b, int c) { return ((a & 0xff) << 16) | ((b & 0xff) << 8) | (c & 0xff); }
};
static QHash<int, Bm> bMetrics;
//---------------------------------------------------------
// initBeamMetrics
//---------------------------------------------------------
#define B(a,b,c,d,e) bMetrics[Bm::key(a, b, c)] = Bm(d, e);
static void initBeamMetrics()
{
// up step1 step2 stemLen1 slant
// (- up) (- up)
// =================================== C
B(1, 10, 10, -12, 0);
B(0, 3, 3, 11, 0);
B(1, 3, 3, -11, 0);
B(1, 10, 9, -12, -1);
B(1, 10, 8, -12, -4);
B(1, 10, 7, -12, -5);
B(1, 10, 6, -15, -5);
B(1, 10, 5, -16, -5);
B(1, 10, 4, -20, -4);
B(1, 10, 3, -20, -5);
B(1, 10, 11, -12, 1);
B(1, 10, 12, -13, 2); // F
B(1, 10, 13, -13, 2);
B(1, 10, 14, -13, 2);
B(1, 10, 15, -13, 2);
B(1, 3, 4, -11, 1);
B(1, 3, 5, -11, 2);
B(1, 3, 6, -11, 4);
B(1, 3, 7, -11, 5);
B(1, 3, 8, -11, 5);
B(1, 3, 9, -11, 5);
B(1, 3, 10, -11, 5);
B(0, -4, -3, 15, 1);
B(0, -4, -2, 15, 2);
B(0, -4, -1, 15, 2);
B(0, -4, 0, 15, 5);
B(0, -4, 1, 16, 5);
B(0, -4, 2, 20, 4);
B(0, -4, 3, 20, 5);
B(0, 3, 4, 13, 1);
B(0, 3, 5, 13, 2);
B(0, 3, 6, 14, 4);
B(0, 3, 7, 13, 4);
B(0, 3, 8, 13, 6);
B(0, 3, 2, 11, -1);
B(0, 3, 1, 11, -2);
B(0, 3, 0, 11, -5);
B(0, 3, -1, 11, -5);
B(0, 3, -2, 11, -5);
B(0, 3, -3, 11, -5);
B(0, 3, -4, 11, -5);
// =================================== D
B(1, 9, 9, -13, 0);
B(0, 2, 2, 12, 0);
B(1, 2, 2, -11, 0);
B(1, 9, 8, -13, -1);
B(1, 9, 7, -13, -2);
B(1, 9, 6, -13, -5);
B(1, 9, 5, -14, -5);
B(1, 9, 4, -16, -6);
B(1, 9, 3, -17, -5);
B(1, 9, 2, -17, -8);
B(1, 9, 10, -11, 1);
B(1, 9, 11, -11, 2);
B(1, 9, 12, -11, 2);
B(1, 9, 13, -11, 2);
B(1, 9, 14, -11, 2);
B(1, 9, 15, -11, 2);
B(1, 2, 3, -12, 1);
B(1, 2, 4, -12, 2);
B(1, 2, 5, -12, 4);
B(1, 2, 6, -12, 5);
B(1, 2, 7, -11, 5);
B(1, 2, 8, -12, 5);
B(1, 2, 9, -12, 8);
B(0, -5,-4, 16, 2);
B(0, -5,-3, 16, 2);
B(0, -5,-2, 16, 2);
B(0, -5,-1, 16, 2);
B(0, -5, 0, 16, 4);
B(0, -5, 1, 16, 5);
B(0, -5, 2, 16, 5);
B(0, 2, 3, 12, 1);
B(0, 2, 4, 12, 4);
B(0, 2, 5, 13, 4); // F
B(0, 2, 6, 15, 5);
B(0, 2, 7, 13, 6);
B(0, 2, 8, 16, 8);
B(0, 2, 9, 16, 8);
B(0, 2, 1, 12, -1);
B(0, 2, 0, 12, -4);
B(0, 2, -1, 12, -5);
B(0, 2, -2, 12, -5);
B(0, 2, -3, 12, -4);
B(0, 2, -4, 12, -4);
B(0, 2, -5, 12, -5);
// =================================== E
B(1, 8, 8, -12, 0);
B(0, 1, 1, 13, 0);
B(1, 1, 1, -9, 0);
B(1, 8, 7, -12, -1);
B(1, 8, 6, -12, -4);
B(1, 8, 5, -12, -5);
B(1, 8, 4, -15, -5);
B(1, 8, 3, -16, -5);
B(1, 8, 2, -17, -6);
B(1, 8, 1, -19, -6);
B(1, 15, 11, -21, -1);
B(1, 15, 10, -21, -1);
B(1, 15, 9, -21, -1);
B(1, 15, 8, -21, -1);
B(1, 1, 8, -11, 6);
B(1, 1, 7, -11, 6);
B(1, 1, 6, -12, 6);
B(1, 8, 9, -12, 1);
B(1, 8, 10, -12, 4);
B(1, 8, 11, -12, 5);
B(1, 8, 12, -12, 5);
B(1, 8, 13, -12, 4);
B(1, 8, 14, -12, 5);
B(1, 8, 15, -12, 1);
B(0, 1, 0, 11, -1);
B(0, 1, -1, 11, -2);
B(0, 1, -2, 11, -5);
B(0, 1, -3, 11, -5);
B(0, 1, -4, 11, -5);
B(0, 1, -5, 11, -5);
B(0, 1, -6, 11, -5);
B(0, 1, 2, 13, 1);
B(0, 1, 3, 13, 2);
B(0, 1, 4, 13, 5);
B(0, 1, 5, 14, 5);
B(0, 1, 6, 15, 5);
B(0, 1, 7, 17, 5);
B(0, 1, 8, 17, 8);
B(0, -6, -2, 19, 2);
B(0, -6, -1, 19, 4);
B(0, -6, 0, 20, 4);
B(0, -6, 1, 20, 5);
B(0, 8, 3, 9, -6);
B(0, 8, 2, 12, -8);
B(0, 8, 1, 12, -8);
// =================================== F
B(1, 7, 7,-13, 0); //F
B(0, 0, 0, 12, 0);
B(0, 7, 7, 10, 0);
B(1, 7, 6, -13, -1);
B(1, 7, 5, -13, -2);
B(1, 7, 4, -13, -5);
B(1, 7, 3, -14, -5);
B(1, 7, 2, -15, -6);
B(1, 7, 1, -17, -6);
B(1, 7, 0, -18, -8);
B(1, 14, 10, -19, -2);
B(1, 14, 9, -19, -2);
B(1, 14, 8, -20, -4);
B(1, 14, 7, -20, -5);
B(1, 0, 5, -9, 6);
B(1, 0, 6, -12, 8);
B(1, 0, 7, -12, 8);
B(1, 7, 8, -11, 1);
B(1, 7, 9, -11, 2);
B(1, 7, 10, -11, 5);
B(1, 7, 11, -11, 5);
B(1, 7, 12, -11, 5);
B(1, 7, 13, -11, 5);
B(1, 7, 14, -11, 5);
B(0, 0, -1, 12, -1);
B(0, 0, -2, 12, -4);
B(0, 0, -3, 12, -5);
B(0, 0, -4, 12, -5);
B(0, 0, -5, 12, -4);
B(0, 0, -6, 12, -4);
B(0, 0, -7, 12, -4);
B(0, 0, 1, 12, 1);
B(0, 0, 2, 12, 4);
B(0, 0, 3, 12, 5);
B(0, 0, 4, 15, 5);
B(0, 0, 5, 16, 5);
B(0, 0, 6, 17, 5);
B(0, 0, 7, 19, 6);
B(0, -7, -3, 21, 2);
B(0, -7, -2, 21, 2);
B(0, -7, -1, 21, 2);
B(0, -7, 0, 22, 4);
B(0, 7, 2, 12, -6);
B(0, 7, 1, 11, -6);
B(0, 7, 0, 11, -6);
// =================================== G
B(1, 6, 6, -12, 0);
B(0, -1, -1, 13, 0);
B(0, 6, 6, 11, 0);
B(1, 6, 5, -12, -1);
B(1, 6, 4, -12, -4);
B(1, 6, 3, -13, -4);
B(1, 6, 2, -15, -5);
B(1, 6, 1, -13, -7);
B(1, 6, 0, -16, -8);
B(1, 6, -1, -16, -8);
B(1, 13, 10, -17, -2);
B(1, 13, 9, -17, -2);
B(1, 13, 8, -18, -4);
B(1, 13, 7, -18, -5);
B(1, 13, 6, -21, -5);
B(1, -1, 6, -10, 8);
B(1, 6, 7, -12, 1);
B(1, 6, 8, -12, 4);
B(1, 6, 9, -12, 5);
B(1, 6, 10, -12, 5);
B(1, 6, 11, -12, 4);
B(1, 6, 12, -12, 5);
B(1, 6, 13, -12, 5);
B(0, -1, -2, 11, -1);
B(0, -1, -3, 11, -2);
B(0, -1, -4, 11, -2);
B(0, -1, -5, 11, -2);
B(0, -1, -6, 11, -2);
B(0, -1, -7, 11, -2);
B(0, -1, 0, 13, 1);
B(0, -1, 1, 13, 2);
B(0, -1, 2, 13, 5);
B(0, -1, 3, 14, 5);
B(0, -1, 4, 17, 6);
B(0, -1, 5, 18, 5);
B(0, -1, 6, 18, 8);
B(0, 6, 5, 12, -4);
B(0, 6, 4, 12, -4);
B(0, 6, 3, 12, -4);
B(0, 6, 2, 12, -6);
B(0, 6, 1, 11, -6);
B(0, 6, 0, 12, -7);
B(0, 6, -1, 12, -8);
// =================================== A
B(1, 5, 5, -11, 0);
B(0, -2, -2, 12, 0);
B(0, 5, 5, 11, 0);
B(1, 5, 4, -13, -1);
B(1, 5, 3, -13, -2);
B(1, 5, 2, -14, -4);
B(1, 5, 1, -14, -4);
B(1, 5, 0, -13, -6);
B(1, 12, 11, -15, -1);
B(1, 12, 10, -15, -2);
B(1, 12, 9, -15, -2);
B(1, 12, 8, -15, -5);
B(1, 12, 7, -16, -5);
B(1, 12, 6, -20, -4);
B(1, 12, 5, -20, -5);
B(1, 5, 6, -11, 1);
B(1, 5, 7, -11, 2);
B(1, 5, 8, -11, 5);
B(1, 5, 9, -11, 5);
B(1, 5, 10, -11, 5);
B(1, 5, 11, -11, 5);
B(1, 5, 12, -11, 5);
B(0, -2, -1, 12, 1);
B(0, -2, 0, 12, 4);
B(0, -2, 1, 12, 5);
B(0, -2, 2, 15, 5);
B(0, -2, 3, 16, 5);
B(0, -2, 4, 20, 4);
B(0, -2, 5, 20, 5);
B(0, -2, -3, 12, -1);
B(0, -2, -4, 13, -2);
B(0, -2, -5, 13, -2);
B(0, -2, -6, 13, -2);
B(0, -2, -7, 13, -2);
B(0, 5, 4, 11, -1);
B(0, 5, 3, 11, -2);
B(0, 5, 2, 11, -4);
B(0, 5, 1, 11, -5);
B(0, 5, 0, 11, -5);
B(0, 5, -1, 11, -5);
B(0, 5, -2, 11, -5);
// =================================== B
B(1, 4, 4, -12, 0);
B(1, 11, 11, -13, 0);
B(0, 4, 4, 12, 0);
B(0, -3, -3, 13, 0);
B(1, 11, 10, -13, -1);
B(1, 11, 9, -13, -2);
B(1, 11, 8, -13, -5);
B(1, 11, 7, -14, -5);
B(1, 11, 6, -18, -4);
B(1, 11, 5, -18, -5);
B(1, 11, 4, -21, -5);
B(1, 4, 3, -12, -1);
B(1, 4, 2, -12, -4);
B(1, 4, 1, -14, -4);
B(1, 4, 0, -16, -4);
B(1, 11, 12, -14, 1);
B(1, 11, 13, -14, 1);
B(1, 11, 14, -14, 1);
B(1, 11, 15, -15, 2);
B(1, 11, 16, -15, 2);
B(1, 4, 5, -12, 1);
B(1, 4, 6, -12, 4);
B(1, 4, 7, -12, 5);
B(1, 4, 8, -12, 5);
B(1, 4, 9, -13, 6);
B(1, 4, 10, -12, 4);
B(1, 4, 11, -12, 5);
B(0, 4, 3, 12, -1);
B(0, 4, 2, 12, -4);
B(0, 4, 1, 12, -5);
B(0, 4, 0, 12, -5);
B(0, 4, -1, 13, -6);
B(0, 4, -2, 12, -4);
B(0, 4, -3, 12, -5);
B(0, 4, 5, 12, 1);
B(0, 4, 6, 12, 4);
B(0, -3, -4, 14, -1);
B(0, -3, -5, 14, -1);
B(0, -3, -6, 14, -1);
B(0, -3, -7, 15, -2);
B(0, -3, -8, 15, -2);
B(0, -3, -9, 15, -2);
B(0, -3, -2, 13, 1);
B(0, -3, -1, 13, 2);
B(0, -3, 0, 13, 5);
B(0, -3, 1, 14, 5);
B(0, -3, 2, 18, 4);
B(0, -3, 3, 18, 5);
B(0, -3, 4, 21, 5);
}
//---------------------------------------------------------
// beamMetric1
// table driven
//---------------------------------------------------------
static Bm beamMetric1(bool up, char l1, char l2)
{
static int initialized = false;
if (!initialized) {
initBeamMetrics();
initialized = true;
}
return bMetrics[Bm::key(up, l1, l2)];
}
//---------------------------------------------------------
// adjust
// adjust stem len for notes between start-end
//---------------------------------------------------------
2012-09-25 13:33:50 +02:00
static int adjust(qreal _spatium4, int slant, const QList<ChordRest*>& cl)
2012-05-26 14:26:10 +02:00
{
2012-09-25 13:33:50 +02:00
int n = cl.size();
2013-01-03 08:42:40 +01:00
const Chord* c1 = 0;
const Chord* c2 = 0;
2012-09-25 13:33:50 +02:00
int i1, i2;
for (i1 = 0; i1 < n; ++i1) {
if (cl[i1]->type() == Element::CHORD) {
c1 = static_cast<Chord*>(cl[i1]);
break;
}
}
for (i2 = n-1; i2 >= 0; --i2) {
if (cl[i2]->type() == Element::CHORD) {
c2 = static_cast<Chord*>(cl[i2]);
break;
}
}
2012-05-26 14:26:10 +02:00
QPointF p1(c1->stemPosBeam()); // canvas coordinates
qreal slope = (slant * _spatium4) / (c2->stemPosBeam().x() - p1.x());
int ml = -1000;
if (c1->up()) {
2012-09-25 13:33:50 +02:00
for (int i = i1+1; i <= i2; ++i) {
const Chord* c = static_cast<Chord*>(cl[i]);
if (c->type() != Element::CHORD)
continue;
QPointF p3(c->stemPosBeam());
2012-05-26 14:26:10 +02:00
qreal yUp = p1.y() + (p3.x() - p1.x()) * slope;
int l = lrint((yUp - p3.y()) / _spatium4);
ml = qMax(ml, l);
}
}
else {
2012-09-25 13:33:50 +02:00
for (int i = i1+1; i <= i2; ++i) {
const Chord* c = static_cast<Chord*>(cl[i]);
if (c->type() != Element::CHORD)
continue;
QPointF p3(c->stemPosBeam());
2012-05-26 14:26:10 +02:00
qreal yUp = p1.y() + (p3.x() - p1.x()) * slope;
int l = lrint((p3.y() - yUp) / _spatium4);
ml = qMax(ml, l);
}
}
return (ml > 0) ? ml : 0;
}
//---------------------------------------------------------
// adjust2
// adjust stem position for single beams
//---------------------------------------------------------
2012-09-25 13:33:50 +02:00
static void adjust2(Bm& bm, const ChordRest* c1)
2012-05-26 14:26:10 +02:00
{
static const int dd[4][4] = {
// St H -- S
{0, 0, 1, 0}, // St
{0, 0, -1, 0}, // S
{1, 1, 1, -1}, // --
{0, 0, -1, 0} // H
};
int ys = bm.l + c1->line() * 2;
int e1 = qAbs((ys + 1000) % 4);
int e2 = qAbs((ys + 1000 + bm.s) % 4);
bm.l -= dd[e1][e2];
}
//---------------------------------------------------------
// minSlant
//---------------------------------------------------------
static int minSlant(uint interval)
{
static const int minSlantTable[] = { 0, 1, 2, 4, 5 };
if (interval > 4)
return 5;
return minSlantTable[interval];
}
//---------------------------------------------------------
// maxSlant
//---------------------------------------------------------
static int maxSlant(uint interval)
{
static const int maxSlantTable[] = { 0, 1, 4, 5, 5, 6, 7, 8 };
if (interval > 7)
return 8;
return maxSlantTable[interval];
}
//---------------------------------------------------------
// slantTable
//---------------------------------------------------------
static int* slantTable(uint interval)
{
static int t[8][5] = {
{ 0, -1, 0, 0, 0 },
{ 1, -1, 0, 0, 0 },
{ 3, 4, 2, -1, 0 },
{ 4, 5, -1, 0, 0 },
{ 5, -1, 0, 0, 0 },
{ 5, 6, -1, 0, 0 },
{ 6, 5, 7, -1, 0 },
{ 6, 7, 5, 8, -1 },
};
if (interval > 7)
interval = 7;
return &t[interval][0] ;
}
//---------------------------------------------------------
// computeStemLen
//---------------------------------------------------------
2012-09-25 13:33:50 +02:00
void Beam::computeStemLen(const QList<ChordRest*>& cl, qreal& py1, int beamLevels)
2012-05-26 14:26:10 +02:00
{
2012-09-25 13:33:50 +02:00
qreal _spatium = spatium();
qreal _spatium4 = _spatium * .25;
qreal _spStaff4 = _spatium4 * staff()->lineDistance(); // scaled to staff line distance for vert. pos. within a staff
2012-09-25 13:33:50 +02:00
const ChordRest* c1 = cl.front();
const ChordRest* c2 = cl.back();
qreal dx = c2->pagePos().x() - c1->pagePos().x();
2012-05-26 14:26:10 +02:00
bool zeroSlant = noSlope(cl);
int l1 = c1->line() * 2;
int l2 = c2->line() * 2;
Bm bm;
if (beamLevels == 1) {
bm = beamMetric1(_up, l1 / 2, l2 / 2);
if (bm.l && !(zeroSlant && cl.size() > 2)) {
if (cl.size() > 2) {
if (_up)
bm.l = -12 - adjust(_spStaff4, bm.s, cl);
2012-05-26 14:26:10 +02:00
else
bm.l = 12 + adjust(_spStaff4, bm.s, cl);
2012-05-26 14:26:10 +02:00
adjust2(bm, c1);
}
}
else {
int* st = slantTable(zeroSlant ? 0 : qAbs((l2 - l1) / 2));
int ll1;
if (_up) {
ll1 = l1 - ((l1 & 3) ? 11 : 12);
int ll1m = l1 - 10;
int rll1 = ll1;
if ((l1 > 20) && (l2 > 20)) {
st = slantTable(zeroSlant ? 0 : 1);
rll1 = (zeroSlant || (l2 < l1)) ? 9 : 8;
}
for (int n = 0; ; ll1--) {
int i;
for (i = 0; st[i] != -1; ++i) {
int slant = (l2 > l1) ? st[i] : -st[i];
int lll1 = qMin(rll1, ll1m - n - adjust(_spStaff4, slant, cl));
2012-05-26 14:26:10 +02:00
int ll2 = lll1 + slant;
static bool ba[4][4] = {
{ true, true, false, true },
{ true, true, false, true },
{ false, false, false, true },
{ true, true, false, true }
};
if (ba[lll1 & 3][ll2 & 3]) {
ll1 = lll1;
bm.s = slant;
break;
}
}
if (st[i] != -1)
break;
if (++n > 4) {
2012-09-25 13:33:50 +02:00
qDebug("beam note not found 1");
2012-05-26 14:26:10 +02:00
break;
}
}
}
else {
ll1 = ((l1 & 3) ? 11 : 12) + l1;
int rll1 = ll1;
if ((l1 < -4) && (l2 < -4)) {
// extend to middle line, slant is always 0 <= 1
st = slantTable(zeroSlant ? 0 : 1);
rll1 = (zeroSlant || (l2 > l1)) ? 7 : 8;
}
for (int n = 0;;ll1++) {
int i;
for (i = 0; st[i] != -1; ++i) {
int slant = (l2 > l1) ? st[i] : -st[i];
int lll1 = qMax(rll1, ll1 + adjust(_spStaff4, slant, cl));
2012-05-26 14:26:10 +02:00
int e1 = lll1 & 3;
int ll2 = lll1 + slant;
int e2 = ll2 & 3;
static bool ba[4][4] = {
{ true, true, false, true },
{ true, true, false, true },
{ false, false, false, true },
{ true, true, false, true }
};
if (ba[e1][e2]) {
ll1 = lll1;
bm.s = slant;
break;
}
}
if (st[i] != -1)
break;
if (++n > 4) {
2012-09-25 13:33:50 +02:00
qDebug("beam not found 2");
2012-05-26 14:26:10 +02:00
break;
}
}
}
bm.l = ll1 - l1;
}
}
else if (beamLevels == 2) {
int minS, maxS;
if (zeroSlant)
minS = maxS = 0;
else {
uint interval = qAbs((l2 - l1) / 2);
minS = minSlant(interval);
maxS = maxSlant(interval);
}
int ll1;
if (_up) {
ll1 = l1 - 12; // sp minimum to primary beam
int rll1 = ll1;
if ((l1 > 20) && (l2 > 20)) {
minS = zeroSlant ? 0 : 1;
maxS = minS;
rll1 = (zeroSlant || (l2 < l1)) ? 9 : 8;
}
for (int n = 0; ; ll1--) {
int i;
for (i = minS; i <= maxS; ++i) {
int slant = (l2 > l1) ? i : -i;
int lll1 = qMin(rll1, ll1 - adjust(_spStaff4, slant, cl));
2012-05-26 14:26:10 +02:00
int ll2 = lll1 + slant;
static bool ba[4][4] = {
{ true, true, false, false },
{ true, true, false, false },
{ false, false, false, false },
{ false, false, false, false }
};
if (ba[lll1 & 3][ll2 & 3]) {
ll1 = lll1;
break;
}
}
if (i <= maxS) {
bm.s = l2 > l1 ? i : -i;
break;
}
if (++n > 4) {
2012-09-25 13:33:50 +02:00
qDebug("beam note not found 1 %d-%d", minS, maxS);
2012-05-26 14:26:10 +02:00
break;
}
}
}
else {
ll1 = 12 + l1;
int rll1 = ll1;
bool down = l2 > l1;
if ((l1 < -4) && (l2 < -4)) {
// extend to middle line, slant is always 0 <= 1
minS = zeroSlant ? 0 : 1;
maxS = minS;
rll1 = (zeroSlant || down) ? 7 : 8;
}
for (int n = 0;;ll1++) {
int i;
for (i = minS; i <= maxS; ++i) {
int slant = down ? i : -i;
int lll1 = qMax(rll1, ll1 + adjust(_spStaff4, slant, cl));
2012-05-26 14:26:10 +02:00
int ll2 = lll1 + slant;
static bool ba[4][4] = {
{ true, false, false, true },
{ false, false, false, false },
{ false, false, false, false },
{ true, false, false, true }
};
if (ba[lll1 & 3][ll2 & 3]) {
ll1 = lll1;
bm.s = slant;
break;
}
}
if (i <= maxS)
break;
if (++n > 4) {
2012-09-25 13:33:50 +02:00
qDebug("beam not found 2");
2012-05-26 14:26:10 +02:00
break;
}
}
}
bm.l = ll1 - l1;
}
else if (beamLevels == 3) {
int slant;
bool outside;
if (zeroSlant) {
outside = (_up && qMin(l1, l2) <= 10) ||
(!_up && qMax(l1, l2) >= 6);
slant = 0;
}
else {
outside = (_up && (l1 <= 10) && (l2 <= 10)) ||
(!_up && (l1 >= 6) && (l2 >= 6));
if (outside)
slant = *slantTable(qAbs(l1-l2) / 2);
else
slant = 4;
if (l1 > l2)
slant = -slant;
}
int ll1;
if (_up) {
static const int t[4] = { 3, 0, 1, 2 };
ll1 = l1 - 15 - adjust(_spStaff4, slant, cl);
2012-05-26 14:26:10 +02:00
ll1 = qMin(ll1, 5);
if (!outside)
ll1 -= t[ll1 & 3]; // extend to sit on line
}
else {
ll1 = 15 + l1 + adjust(_spStaff4, slant, cl);
2012-05-26 14:26:10 +02:00
ll1 = qMax(ll1, 11);
if (!outside)
ll1 += 3 - (ll1 & 3); // extend to hang on line
}
bm.s = slant;
bm.l = ll1 - l1;
}
else if (beamLevels == 4) {
int slant = zeroSlant ? 0 : (l2 > l1 ? 4 : -4);
int ll1;
if (_up) {
ll1 = l1 - 17 - adjust(_spStaff4, slant, cl);
2012-05-26 14:26:10 +02:00
ll1 = qMin(ll1, 1);
static const int t[4] = { 3, 0, 1, 2 };
ll1 -= t[ll1 & 3]; // extend to sit on line
}
else {
ll1 = 17 + l1 + adjust(_spStaff4, slant, cl);
2012-05-26 14:26:10 +02:00
ll1 = qMax(ll1, 15);
ll1 += 3 - (ll1 & 3); // extend to hang on line
}
bm.s = slant;
bm.l = ll1 - l1;
}
else { // if (beamLevels > 4) {
static const int t[] = { 0, 0, 4, 4, 8, 12, 16 }; // spatium4 added to stem len
int n = t[beamLevels] + 12;
bm.s = 0;
if (_up) {
bm.l = -n;
bm.l -= adjust(_spStaff4, bm.s, cl);
2012-05-26 14:26:10 +02:00
}
else {
bm.l += n;
bm.l += adjust(_spStaff4, bm.s, cl);
2012-05-26 14:26:10 +02:00
}
}
2012-09-25 13:33:50 +02:00
if (dx == 0.0)
slope = 0.0;
else
slope = (bm.s * _spatium4) / dx;
py1 += ((c1->line(_up) - c1->line(!_up)) * 2 + bm.l) * _spStaff4;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layout2
//---------------------------------------------------------
2012-07-27 18:01:15 +02:00
void Beam::layout2(QList<ChordRest*>crl, SpannerSegmentType, int frag)
2012-05-26 14:26:10 +02:00
{
if (_distribute)
score()->respace(&crl); // fix horizontal spacing of stems
2012-09-25 13:33:50 +02:00
if (crl.isEmpty()) // no beamed Elements
2012-05-26 14:26:10 +02:00
return;
2012-09-25 13:33:50 +02:00
const ChordRest* c1 = crl.front(); // first chord/rest in beam
const ChordRest* c2 = crl.back(); // last chord/rest in beam
2012-05-26 14:26:10 +02:00
int beamLevels = 1;
2012-09-25 13:33:50 +02:00
foreach(ChordRest* c, crl) {
int bl = c->durationType().hooks();
beamLevels = qMax(beamLevels, bl);
2012-05-26 14:26:10 +02:00
}
BeamFragment* f = fragments[frag];
2012-08-04 15:46:43 +02:00
int dIdx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
qreal& py1 = f->py1[dIdx];
qreal& py2 = f->py2[dIdx];
2012-11-11 15:04:25 +01:00
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
2013-01-02 14:33:23 +01:00
QPointF _pagePos(pagePos());
2012-05-26 14:26:10 +02:00
qreal beamMinLen = point(score()->styleS(ST_beamMinLen));
qreal graceMag = score()->styleD(ST_graceNoteMag);
// style values ST_beamDistance and ST_beamWidth not used
if (beamLevels == 4)
_beamDist = (2.5 / 3.0) * _spatium;
else
_beamDist = 0.75 * _spatium;
if (isGrace) {
_beamDist *= graceMag;
setMag(graceMag);
beamMinLen *= graceMag;
}
else
setMag(1.0);
2013-01-29 16:26:24 +01:00
int n = crl.size();
StaffTypeTablature* tab = 0;
if (staff()->isTabStaff() )
tab = (StaffTypeTablature*)staff()->staffType();
if (tab && !tab->stemThrough() ) {
//
// TAB STAVES with stems beside staves: beam position is fixed depending on TAB parameters and chordrest up/down
// (all the chordrests of a beam have the same up/down, as it depends on TAB parameters if there are no voices
// or from the voice the beam belongs to if there are voices; then, it is enough to check only the first chordrest)
_up = c1->up();
// compute vert. pos. of beam, relative to staff (top line = 0)
qreal y = tab->chordRestStemPosY(c1) + (_up ? - STAFFTYPE_TAB_DEFAULTSTEMLEN_UP : STAFFTYPE_TAB_DEFAULTSTEMLEN_DN);
y *= _spatium;
py1 = py2 = y; // in this case, beams are always horizontal: py1 = py2
2012-05-26 14:26:10 +02:00
}
else {
//
// PITCHED STAVES (or TAB with stems through staves)
2012-05-26 14:26:10 +02:00
//
2013-01-02 20:13:58 +01:00
qreal px1 = c1->stemPosX();
qreal px2 = c2->stemPosX();
2012-05-26 14:26:10 +02:00
if (_userModified[dIdx]) {
2013-01-02 14:33:23 +01:00
py1 += _pagePos.y();
py2 += _pagePos.y();
2012-05-26 14:26:10 +02:00
2012-06-01 14:33:20 +02:00
qreal beamY = py1;
slope = (py2 - py1) / (px2 - px1);
2012-05-26 14:26:10 +02:00
//
// set stem direction for every chord
//
2013-01-29 16:26:24 +01:00
for (int i = 0; i < n; ++i) {
Chord* c = static_cast<Chord*>(crl.at(i));
if (c->type() != CHORD)
2012-09-25 13:33:50 +02:00
continue;
2012-05-26 14:26:10 +02:00
QPointF p = c->upNote()->pagePos();
qreal y1 = beamY + (p.x() - px1) * slope;
bool nup = y1 < p.y();
if (c->up() != nup) {
c->setUp(nup);
// guess was wrong, have to relayout
score()->layoutChords1(c->segment(), c->staffIdx());
}
}
2012-09-25 13:33:50 +02:00
_up = crl.front()->up();
2012-05-26 14:26:10 +02:00
}
else if (cross) {
qreal beamY = 0.0; // y position of main beam start
qreal y1 = -200000;
qreal y2 = 200000;
2013-01-29 16:26:24 +01:00
for (int i = 0; i < n; ++i) {
Chord* c = static_cast<Chord*>(crl.at(i));
if (c->type() != CHORD)
continue;
qreal y = c->upNote()->pagePos().y();
y1 = qMax(y1, y);
y2 = qMin(y2, y);
2012-05-26 14:26:10 +02:00
}
if (y1 > y2)
beamY = y2 + (y1 - y2) * .5;
else
beamY = _up ? y2 : y1;
py1 = beamY;
2012-11-11 15:04:25 +01:00
2012-05-26 14:26:10 +02:00
//
// set stem direction for every chord
//
2013-01-29 16:26:24 +01:00
for (int i = 0; i < n; ++i) {
Chord* c = static_cast<Chord*>(crl.at(i));
if (c->type() != CHORD)
2012-09-25 13:33:50 +02:00
continue;
2012-05-26 14:26:10 +02:00
qreal y = c->upNote()->pagePos().y();
bool nup = beamY < y;
if (c->up() != nup) {
c->setUp(nup);
// guess was wrong, have to relayout
score()->layoutChords1(c->segment(), c->staffIdx());
}
}
qreal yDownMax = -300000;
qreal yUpMin = 300000;
2013-01-29 16:26:24 +01:00
for (int i = 0; i < n; ++i) {
Chord* c = static_cast<Chord*>(crl.at(i));
if (c->type() != CHORD)
2012-09-25 13:33:50 +02:00
continue;
bool _up = c->up();
2013-01-02 14:33:23 +01:00
qreal y = (_up ? c->upNote() : c->downNote())->pagePos().y();
2012-05-26 14:26:10 +02:00
if (_up)
yUpMin = qMin(y, yUpMin);
else
yDownMax = qMax(y, yDownMax);
}
2012-06-01 14:33:20 +02:00
qreal slant = _spatium;
2012-09-25 13:33:50 +02:00
if (crl.front()->up())
2012-06-01 14:33:20 +02:00
slant = -slant;
py1 = yUpMin + (yDownMax - yUpMin) * .5 - slant * .5;
slope = slant / (px2 - px1);
2012-05-26 14:26:10 +02:00
}
else {
py1 = c1->stemPos().y();
py2 = c2->stemPos().y();
2012-09-25 13:33:50 +02:00
computeStemLen(crl, py1, beamLevels);
2012-05-26 14:26:10 +02:00
}
py2 = (px2 - px1) * slope + py1;
2013-01-02 14:33:23 +01:00
py1 -= _pagePos.y();
py2 -= _pagePos.y();
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------
// create beam segments
2012-05-26 14:26:10 +02:00
//---------------------------------------------
2013-01-02 14:33:23 +01:00
qreal x1 = crl[0]->stemPosX() - pageX();
2012-05-26 14:26:10 +02:00
int baseLevel = 0;
2012-05-26 14:26:10 +02:00
for (int beamLevel = 0; beamLevel < beamLevels; ++beamLevel) {
bool growDown = _up || cross;
for (int i = 0; i < n; ++i) {
2012-09-25 13:33:50 +02:00
ChordRest* cr1 = crl[i];
int l = cr1->durationType().hooks() - 1;
if (l >= beamLevel) {
int c1 = i;
++i;
for (; i < n; ++i) {
2012-09-25 13:33:50 +02:00
ChordRest* c = crl[i];
int l = c->durationType().hooks() - 1;
bool b32 = (beamLevel >= 1) && (c->beamMode() == BeamMode::BEGIN32);
bool b64 = (beamLevel >= 2) && (c->beamMode() == BeamMode::BEGIN64);
if (l >= beamLevel && (b32 || b64)) {
++i;
break;
2012-05-26 14:26:10 +02:00
}
if (l < beamLevel)
break;
2012-05-26 14:26:10 +02:00
}
int bl = growDown ? beamLevel : -beamLevel;
2012-09-25 13:33:50 +02:00
ChordRest* cr2 = crl[i-1];
if (c1 && (cr1->up() == cr2->up())) {
QPointF stemPos(cr1->stemPos());
2013-01-02 14:33:23 +01:00
qreal x = stemPos.x() - _pagePos.x();
qreal x2 = x - _pagePos.x();
qreal y1 = (x2 - x1) * slope + py1 + _pagePos.y();
qreal y2 = cr1->stemPos().y();
if ((y1 < y2) != growDown)
bl = baseLevel - (beamLevel + 1);
}
int c2 = i;
if (c1 == 0 && c2 == n)
++baseLevel;
2012-05-26 14:26:10 +02:00
qreal stemWidth = point(score()->styleS(ST_stemWidth));
2013-01-02 14:33:23 +01:00
qreal x2 = cr1->stemPosX() - _pagePos.x();
qreal x3;
if ((c2 - c1) > 1) {
2012-09-25 13:33:50 +02:00
ChordRest* cr2 = crl[c2-1];
// create segment
2013-01-02 14:33:23 +01:00
x3 = cr2->stemPosX() - _pagePos.x();
2012-05-26 14:26:10 +02:00
if (tab) {
x2 -= stemWidth * 0.5;
x3 += stemWidth * 0.5;
}
else {
if (cr1->up())
x2 -= stemWidth;
else
x3 += stemWidth;
}
2012-05-26 14:26:10 +02:00
}
else {
// create broken segment
2012-09-25 13:33:50 +02:00
int n = crl.size();
qreal len = point(score()->styleS(ST_beamMinLen));
2012-05-26 14:26:10 +02:00
//
2012-11-30 01:08:00 +01:00
// find direction (by default, segment points to right)
//
// if first or last of group
// unconditionally set beam at right or left side
if (c1 == 0) // first => point to right
;
else if (c1 == n - 1) // last => point to left
len = -len;
else {
// if inside group
// PRO: this algorithm is simple(r) and finds the right direction in
// the great majority of cases, without attempting to 'understand'
// neither the rhythm nor the time signature
// CON: it fails in some highly subdivided tuplets (9-plet or more) or sub-tuplets.
// Compute the position in the measure of the end of this
// (i.e. of the beginning of next chord)
int measTick = cr1->measure()->tick();
int tickNext = crl[c1+1]->tick() - measTick;
// determine the tick length of a chord with one beam level less than this
// (i.e. twice the ticks of this)
int tickMod = (tickNext - (crl[c1]->tick() - measTick)) * 2;
// if this completes, within the measure, a unit of tickMod length, flip beam to left
// (allow some tolerance for tick rounding in tuplets
// without tuplet tolerance, could be simplified to:)
// if (tickNext % tickMod == 0)
#define BEAM_TUPLET_TOLERANCE 6
int mod = tickNext % tickMod;
if (mod <= BEAM_TUPLET_TOLERANCE || (tickMod - mod) <= BEAM_TUPLET_TOLERANCE)
2012-05-26 14:26:10 +02:00
len = -len;
}
if (tab) {
if (len > 0)
x2 -= stemWidth * 0.5;
else
x2 += stemWidth * 0.5;
}
else {
bool stemUp = cr1->up();
if (stemUp && len > 0)
x2 -= stemWidth;
else if (!stemUp && len < 0)
x2 += stemWidth;
}
x3 = x2 + len;
}
//feathered beams
qreal yo = py1 + bl * _beamDist * _grow1;
qreal yoo = py1 + bl * _beamDist * _grow2;
qreal ly1 = (x2 - x1) * slope + yo;
qreal ly2 = (x3 - x1) * slope + yoo;
2012-08-23 12:28:27 +02:00
if (!qIsFinite(x2) || !qIsFinite(ly1)
2012-09-25 13:33:50 +02:00
|| !qIsFinite(x3) || !qIsFinite(ly2)) {
qDebug("bad beam segment: slope %f", slope);
}
2012-11-11 15:04:25 +01:00
else {
2012-08-23 12:28:27 +02:00
beamSegments.push_back(new QLineF(x2, ly1, x3, ly2));
2012-11-11 15:04:25 +01:00
}
--i;
}
}
}
//
// create stems
//
for (int i = 0; i < n; ++i) {
2013-01-29 16:26:24 +01:00
Chord* c = static_cast<Chord*>(crl[i]);
if (c->type() != CHORD)
2012-09-25 13:33:50 +02:00
continue;
Stem* stem = c->stem();
if (!stem) {
stem = new Stem(score());
2012-09-25 13:33:50 +02:00
c->setStem(stem);
}
2012-09-25 13:33:50 +02:00
if (c->hook())
score()->undoRemoveElement(c->hook());
2012-09-25 13:33:50 +02:00
QPointF stemPos(c->stemPos());
2013-01-02 20:13:58 +01:00
qreal x2 = stemPos.x() - _pagePos.x();
2013-01-02 14:33:23 +01:00
qreal y1 = (x2 - x1) * slope + py1 + _pagePos.y();
qreal y2 = stemPos.y();
qreal fuzz = _spatium * .1;
qreal by = y2 < y1 ? -1000000 : 1000000;
2013-01-29 16:26:24 +01:00
foreach (const QLineF* l, beamSegments) {
if ((x2+fuzz) >= l->x1() && (x2-fuzz) <= l->x2()) {
qreal y = (x2 - l->x1()) * slope + l->y1();
by = y2 < y1 ? qMax(by, y) : qMin(by, y);
2012-05-26 14:26:10 +02:00
}
}
if (by == -1000000 || by == 1000000) {
2013-01-25 21:17:04 +01:00
if (beamSegments.isEmpty())
qDebug("no BeamSegments");
else {
qDebug("BeamSegment not found: x %f %f-%f",
x2, beamSegments.front()->x1(),
beamSegments.back()->x2());
}
}
stem->setLen(y2 - (by + _pagePos.y()));
if (!tab) {
bool _up = c->up();
qreal stemWidth5 = stem->lineWidth() * .5;
qreal noteWidth = c->notes().size() ? c->notes().at(0)->headWidth() :
symbols[score()->symIdx()][quartheadSym].width(magS());
qreal stemX;
if (_up)
stemX = noteWidth - stemWidth5;
else
stemX = stemWidth5;
stem->rxpos() = stemX;
}
2013-01-02 14:33:23 +01:00
//
// layout stem slash for acciacatura
//
2012-09-25 13:33:50 +02:00
if ((i == 0) && c->noteType() == NOTE_ACCIACCATURA) {
StemSlash* stemSlash = c->stemSlash();
if (!stemSlash) {
stemSlash = new StemSlash(score());
2012-09-25 13:33:50 +02:00
c->add(stemSlash);
}
stemSlash->layout();
}
else
2012-09-25 13:33:50 +02:00
c->setStemSlash(0);
2012-09-25 13:33:50 +02:00
Tremolo* tremolo = c->tremolo();
if (tremolo)
tremolo->layout();
2012-05-26 14:26:10 +02:00
}
}
2012-11-08 14:01:59 +01:00
//---------------------------------------------------------
// spatiumChanged
//---------------------------------------------------------
void Beam::spatiumChanged(qreal oldValue, qreal newValue)
{
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
if (_userModified[idx]) {
qreal diff = newValue / oldValue;
foreach(BeamFragment* f, fragments) {
f->py1[idx] = f->py1[idx] * diff;
f->py2[idx] = f->py2[idx] * diff;
}
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// write
//---------------------------------------------------------
void Beam::write(Xml& xml) const
{
if (_elements.isEmpty())
return;
xml.stag(QString("Beam id=\"%1\"").arg(_id));
Element::writeProperties(xml);
2012-08-12 11:44:36 +02:00
writeProperty(xml, P_STEM_DIRECTION);
writeProperty(xml, P_DISTRIBUTE);
writeProperty(xml, P_GROW_LEFT);
writeProperty(xml, P_GROW_RIGHT);
2012-05-26 14:26:10 +02:00
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
if (_userModified[idx]) {
qreal _spatium = spatium();
foreach(BeamFragment* f, fragments) {
xml.stag("Fragment");
xml.tag("y1", f->py1[idx] / _spatium);
xml.tag("y2", f->py2[idx] / _spatium);
xml.etag();
}
}
#ifndef NDEBUG
//
// this info is used for regression testing
// l1/l2 is the beam position of the layout engine
//
if (score()->testMode()) {
qreal _spatium4 = spatium() * .25;
foreach(BeamFragment* f, fragments) {
xml.tag("l1", int(lrint(f->py1[idx] / _spatium4)));
xml.tag("l2", int(lrint(f->py2[idx] / _spatium4)));
}
}
#endif
xml.etag();
}
//---------------------------------------------------------
// read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Beam::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
QPointF p1, p2;
qreal _spatium = spatium();
2013-01-11 18:10:18 +01:00
_id = e.intAttribute("id");
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "StemDirection") {
2012-05-26 14:26:10 +02:00
setProperty(P_STEM_DIRECTION, ::getProperty(P_STEM_DIRECTION, e));
e.readNext();
}
2012-05-26 14:26:10 +02:00
else if (tag == "distribute")
2013-01-11 18:10:18 +01:00
setDistribute(e.readInt());
2012-05-26 14:26:10 +02:00
else if (tag == "growLeft")
2013-01-11 18:10:18 +01:00
setGrowLeft(e.readDouble());
2012-05-26 14:26:10 +02:00
else if (tag == "growRight")
2013-01-11 18:10:18 +01:00
setGrowRight(e.readDouble());
2012-05-26 14:26:10 +02:00
else if (tag == "y1") {
if (fragments.isEmpty())
fragments.append(new BeamFragment);
BeamFragment* f = fragments.back();
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
_userModified[idx] = true;
2013-01-11 18:10:18 +01:00
f->py1[idx] = e.readDouble() * _spatium;
2012-05-26 14:26:10 +02:00
}
else if (tag == "y2") {
if (fragments.isEmpty())
fragments.append(new BeamFragment);
BeamFragment* f = fragments.back();
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
_userModified[idx] = true;
2013-01-11 18:10:18 +01:00
f->py2[idx] = e.readDouble() * _spatium;
2012-05-26 14:26:10 +02:00
}
else if (tag == "Fragment") {
BeamFragment* f = new BeamFragment;
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
_userModified[idx] = true;
qreal _spatium = spatium();
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
2012-05-26 14:26:10 +02:00
if (tag == "y1")
f->py1[idx] = e.readDouble() * _spatium;
2012-05-26 14:26:10 +02:00
else if (tag == "y2")
f->py2[idx] = e.readDouble() * _spatium;
2012-05-26 14:26:10 +02:00
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
fragments.append(f);
}
#ifndef NDEBUG
else if (tag == "l1" || tag == "l2") // ignore
2013-01-17 12:56:14 +01:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
#endif
else if (tag == "subtype") // obsolete
2013-01-17 12:56:14 +01:00
e.skipCurrentElement();
2012-05-26 14:26:10 +02:00
else if (!Element::readProperties(e))
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// editDrag
//---------------------------------------------------------
void Beam::editDrag(const EditData& ed)
{
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
qreal dy = ed.delta.y();
BeamFragment* f = fragments[editFragment];
if (ed.curGrip == 0)
f->py1[idx] += dy;
f->py2[idx] += dy;
_userModified[idx] = true;
setGenerated(false);
layout1();
layout();
}
//---------------------------------------------------------
// updateGrips
//---------------------------------------------------------
void Beam::updateGrips(int* grips, QRectF* grip) const
{
*grips = 2;
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
BeamFragment* f = fragments[editFragment];
Chord* c1;
Chord* c2;
int n = _elements.size();
for (int i = 0; i < n; ++i) {
if (_elements[i]->type() == CHORD) {
c1 = static_cast<Chord*>(_elements[i]);
break;
}
}
for (int i = n-1; i >= 0; --i) {
if (_elements[i]->type() == CHORD) {
c2 = static_cast<Chord*>(_elements[i]);
break;
}
}
int y = pagePos().y();
2013-01-02 20:13:58 +01:00
grip[0].translate(QPointF(c1->stemPosX(), f->py1[idx] + y));
grip[1].translate(QPointF(c2->stemPosX(), f->py2[idx] + y));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setBeamDirection
//---------------------------------------------------------
2012-08-04 15:46:43 +02:00
void Beam::setBeamDirection(MScore::Direction d)
2012-05-26 14:26:10 +02:00
{
_direction = d;
2012-08-04 15:46:43 +02:00
if (d != MScore::AUTO)
_up = d == MScore::UP;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Beam::reset()
2012-05-26 14:26:10 +02:00
{
if (distribute())
score()->undoChangeProperty(this, P_DISTRIBUTE, false);
if (growLeft() != 1.0)
score()->undoChangeProperty(this, P_GROW_LEFT, 1.0);
if (growRight() != 1.0)
score()->undoChangeProperty(this, P_GROW_RIGHT, 1.0);
if (userModified()) {
score()->undoChangeProperty(this, P_BEAM_POS, QVariant(beamPos()));
score()->undoChangeProperty(this, P_USER_MODIFIED, false);
}
2012-08-04 15:46:43 +02:00
if (beamDirection() != MScore::AUTO)
score()->undoChangeProperty(this, P_STEM_DIRECTION, int(MScore::AUTO));
2012-05-26 14:26:10 +02:00
setGenerated(true);
}
//---------------------------------------------------------
// startEdit
//---------------------------------------------------------
void Beam::startEdit(MuseScoreView*, const QPointF& p)
{
QPointF pt(p - pagePos());
qreal ydiff = 100000000.0;
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
int i = 0;
editFragment = 0;
foreach (BeamFragment* f, fragments) {
qreal d = fabs(f->py1[idx] - pt.y());
if (d < ydiff) {
ydiff = d;
editFragment = i;
}
++i;
}
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
bool Beam::acceptDrop(MuseScoreView*, const QPointF&, Element* e) const
{
return (e->type() == ICON) && ((static_cast<Icon*>(e)->iconType() == ICON_FBEAM1)
|| (static_cast<Icon*>(e)->iconType() == ICON_FBEAM2));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Beam::drop(const DropData& data)
{
Icon* e = static_cast<Icon*>(data.element);
if (e->type() != ICON)
return 0;
qreal g1;
qreal g2;
if (e->iconType() == ICON_FBEAM1) {
2012-05-26 14:26:10 +02:00
g1 = 1.0;
g2 = 0.0;
}
else if (e->iconType() == ICON_FBEAM2) {
2012-05-26 14:26:10 +02:00
g1 = 0.0;
g2 = 1.0;
}
else
return 0;
if (g1 != growLeft())
score()->undoChangeProperty(this, P_GROW_LEFT, g1);
if (g2 != growRight())
score()->undoChangeProperty(this, P_GROW_RIGHT, g2);
return 0;
}
//---------------------------------------------------------
// beamPos
// misuse QPointF for y1-y2 real values
//---------------------------------------------------------
QPointF Beam::beamPos() const
{
if (fragments.isEmpty())
return QPointF(0.0, 0.0);
BeamFragment* f = fragments.back();
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
qreal _spatium = spatium();
return QPointF(f->py1[idx] / _spatium, f->py2[idx] / _spatium);
}
//---------------------------------------------------------
// setBeamPos
//---------------------------------------------------------
void Beam::setBeamPos(const QPointF& bp)
{
if (fragments.isEmpty())
fragments.append(new BeamFragment);
BeamFragment* f = fragments.back();
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
_userModified[idx] = true;
setGenerated(false);
qreal _spatium = spatium();
f->py1[idx] = bp.x() * _spatium;
f->py2[idx] = bp.y() * _spatium;
}
//---------------------------------------------------------
// userModified
//---------------------------------------------------------
bool Beam::userModified() const
{
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
return _userModified[idx];
}
//---------------------------------------------------------
// setUserModified
//---------------------------------------------------------
void Beam::setUserModified(bool val)
{
2012-08-04 15:46:43 +02:00
int idx = (_direction == MScore::AUTO || _direction == MScore::DOWN) ? 0 : 1;
2012-05-26 14:26:10 +02:00
_userModified[idx] = val;
}
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Beam::getProperty(P_ID propertyId) const
{
switch(propertyId) {
case P_STEM_DIRECTION: return int(beamDirection());
case P_DISTRIBUTE: return distribute();
case P_GROW_LEFT: return growLeft();
case P_GROW_RIGHT: return growRight();
case P_USER_MODIFIED: return userModified();
case P_BEAM_POS: return beamPos();
default:
return Element::getProperty(propertyId);
}
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Beam::setProperty(P_ID propertyId, const QVariant& v)
{
switch(propertyId) {
case P_STEM_DIRECTION:
2012-08-04 15:46:43 +02:00
setBeamDirection(MScore::Direction(v.toInt()));
2012-05-26 14:26:10 +02:00
break;
case P_DISTRIBUTE:
setDistribute(v.toBool());
break;
case P_GROW_LEFT:
setGrowLeft(v.toDouble());
break;
case P_GROW_RIGHT:
setGrowRight(v.toDouble());
break;
case P_USER_MODIFIED:
setUserModified(v.toBool());
break;
case P_BEAM_POS:
if (userModified())
setBeamPos(v.toPointF());
break;
default:
if (!Element::setProperty(propertyId, v))
return false;
break;
}
score()->setLayoutAll(true);
setGenerated(false);
return true;
}
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Beam::propertyDefault(P_ID id) const
{
switch(id) {
2012-08-04 15:46:43 +02:00
case P_STEM_DIRECTION: return int(MScore::AUTO);
2012-05-26 14:26:10 +02:00
case P_DISTRIBUTE: return false;
case P_GROW_LEFT: return 1.0;
case P_GROW_RIGHT: return 1.0;
case P_USER_MODIFIED: return false;
case P_BEAM_POS: return beamPos();
default: return Element::propertyDefault(id);
}
}