layout optimizations

This commit is contained in:
ws 2016-05-02 13:41:41 +02:00
parent 2aec0b81b4
commit 32c3100f80
17 changed files with 194 additions and 237 deletions

View file

@ -189,7 +189,7 @@ void BarLine::getY(qreal* y1, qreal* y2) const
staffIdx2 = score()->nstaves() - 1;
}
Measure* measure = 0;
System* system;
System* system = 0;
SysStaff* sysStaff0 = 0; // top staff for barline in system
bool systemBarLine;
if (parent()->type() == Element::Type::SEGMENT) {

View file

@ -3156,7 +3156,6 @@ Element* Chord::prevElement()
return e;
}
}
return ChordRest::prevElement();
}

View file

@ -767,7 +767,7 @@ Element* ChordRest::drop(const DropData& data)
switch (e->type()) {
case Element::Type::BREATH:
{
Breath* b = static_cast<Breath*>(e);
Breath* b = toBreath(e);
int track = staffIdx() * VOICES;
b->setTrack(track);

View file

@ -75,6 +75,7 @@ class SlurSegment;
class Beam;
class Hook;
class StemSlash;
class Spacer;
enum class SymId;
@ -664,6 +665,7 @@ class Element : public QObject, public ScoreElement {
CONVERT(Hook, HOOK);
CONVERT(StemSlash, STEM_SLASH);
CONVERT(SlurSegment, SLUR_SEGMENT);
CONVERT(Spacer, SPACER);
#undef CONVERT
};
@ -726,6 +728,7 @@ static inline const a* to##a(const Element* e) { Q_ASSERT(e == 0 || e->type() ==
CONVERT(Hook, HOOK);
CONVERT(StemSlash, STEM_SLASH);
CONVERT(SlurSegment, SLUR_SEGMENT);
CONVERT(Spacer, SPACER);
#undef CONVERT
//---------------------------------------------------------

View file

@ -1369,7 +1369,7 @@ void Score::addSystemHeader(Measure* m, bool isFirstSystem)
BarLine* bl = 0;
Segment* s = m->findSegment(Segment::Type::BeginBarLine, tick);
if (s && s->isBeginBarLineType())
if (s)
bl = toBarLine(s->element(0));
if ((nVisible > 1 && score()->styleB(StyleIdx::startBarlineMultiple)) || (nVisible <= 1 && score()->styleB(StyleIdx::startBarlineSingle))) {
@ -1836,51 +1836,26 @@ static void layoutPage(Page* page, qreal restHeight)
}
//---------------------------------------------------------
// sff
// compute 1/Force for a given Extend
// Spring
//---------------------------------------------------------
qreal sff(qreal x, qreal xMin, const SpringMap& springs)
{
if (x <= xMin)
return 0.0;
auto i = springs.begin();
qreal c = i->second.stretch;
if (c == 0.0) //DEBUG
c = 1.1;
qreal f = 0.0;
for (; i != springs.end();) {
xMin -= i->second.fix;
f = (x - xMin) / c;
++i;
if (i == springs.end() || f <= i->first)
break;
c += i->second.stretch;
}
return f;
}
//---------------------------------------------------------
// Spring2
//---------------------------------------------------------
struct Spring2 {
struct Spring {
int seg;
qreal stretch;
qreal fix;
Spring2(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
Spring(int i, qreal s, qreal f) : seg(i), stretch(s), fix(f) {}
};
typedef std::multimap<qreal, Spring2, std::less<qreal> > SpringMap2;
typedef std::multimap<qreal, Spring, std::less<qreal> > SpringMap;
//---------------------------------------------------------
// sff2
// compute 1/Force for a given Extend
//---------------------------------------------------------
qreal sff2(qreal x, qreal xMin, const SpringMap2& springs)
static qreal sff2(qreal width, qreal xMin, const SpringMap& springs)
{
if (x <= xMin)
if (width <= xMin)
return 0.0;
auto i = springs.begin();
qreal c = i->second.stretch;
@ -1889,7 +1864,7 @@ qreal sff2(qreal x, qreal xMin, const SpringMap2& springs)
qreal f = 0.0;
for (; i != springs.end();) {
xMin -= i->second.fix;
f = (x - xMin) / c;
f = (width - xMin) / c;
++i;
if (i == springs.end() || f <= i->first)
break;
@ -1898,7 +1873,6 @@ qreal sff2(qreal x, qreal xMin, const SpringMap2& springs)
return f;
}
//---------------------------------------------------------
// respace
//---------------------------------------------------------
@ -1927,7 +1901,7 @@ void Score::respace(std::vector<ChordRest*>* elements)
// compute stretches
//---------------------------------------------------
SpringMap2 springs;
SpringMap springs;
qreal minimum = 0.0;
for (int i = 0; i < n-1; ++i) {
qreal w = width[i];
@ -1935,7 +1909,7 @@ void Score::respace(std::vector<ChordRest*>* elements)
qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
qreal d = w / str;
springs.insert(std::pair<qreal, Spring2>(d, Spring2(i, str, w)));
springs.insert(std::pair<qreal, Spring>(d, Spring(i, str, w)));
minimum += w;
}
@ -2680,11 +2654,8 @@ void Score::getNextMeasure(LayoutContext& lc)
}
else if (isMaster() && s->isChordRestType()) {
for (Element* e : s->annotations()) {
if (e->isTempoText()) {
TempoText* tt = toTempoText(e);
setTempo(tt->segment(), tt->tempo());
}
e->layout();
if (!e->isTempoText()) // layout tempotext after stretchMeasure
e->layout();
}
qreal stretch = 0.0;
for (Element* e : s->elist()) {
@ -3250,6 +3221,14 @@ System* Score::collectSystem(LayoutContext& lc)
cr->beam()->layout();
}
}
for (Element* e : s->annotations()) {
if (e->isTempoText()) {
TempoText* tt = toTempoText(e);
setTempo(tt->segment(), tt->tempo());
tt->layout();
s->staffShape(tt->staffIdx()).add(tt->shape());
}
}
}
}
system->layout2();

View file

@ -2,7 +2,7 @@
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2011 Werner Schweer
// Copyright (C) 2002-2016 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
@ -17,17 +17,6 @@ namespace Ms {
class Segment;
//---------------------------------------------------------
// Spring
//---------------------------------------------------------
struct Spring {
Segment* seg;
qreal stretch;
qreal fix;
Spring(Segment* s, qreal str) : seg(s), stretch(str), fix(s->width()) {}
};
//---------------------------------------------------------
// LayoutContext
// temp values used during layout
@ -58,10 +47,6 @@ struct LayoutContext {
int adjustMeasureNo(MeasureBase*);
};
typedef std::multimap<qreal, Spring, std::less<qreal> > SpringMap;
extern qreal sff(qreal x, qreal xMin, const SpringMap& springs);
} // namespace Ms
#endif

View file

@ -620,8 +620,8 @@ void Measure::add(Element* e)
break;
case Element::Type::SEGMENT:
{
Segment* seg = static_cast<Segment*>(e);
int t = seg->tick();
Segment* seg = toSegment(e);
int t = seg->tick();
Segment::Type st = seg->segmentType();
Segment* s;
for (s = first(); s && s->tick() < t; s = s->next())
@ -675,7 +675,6 @@ void Measure::add(Element* e)
MeasureBase::add(e);
break;
}
}
//---------------------------------------------------------
@ -694,9 +693,9 @@ void Measure::remove(Element* e)
break;
case Element::Type::SPACER:
if (static_cast<Spacer*>(e)->spacerType() == SpacerType::DOWN)
if (toSpacer(e)->spacerType() == SpacerType::DOWN)
_mstaves[e->staffIdx()]->_vspacerDown = 0;
else if (static_cast<Spacer*>(e)->spacerType() == SpacerType::UP)
else if (toSpacer(e)->spacerType() == SpacerType::UP)
_mstaves[e->staffIdx()]->_vspacerUp = 0;
break;
@ -3199,6 +3198,10 @@ qreal Measure::userStretch() const
return (score()->layoutMode() == LayoutMode::FLOAT ? 1.0 : _userStretch);
}
//---------------------------------------------------------
// nextElementStaff
//---------------------------------------------------------
Element* Measure::nextElementStaff(int staff)
{
Segment* firstSeg = segments().first();
@ -3207,6 +3210,10 @@ Element* Measure::nextElementStaff(int staff)
return score()->firstElement();
}
//---------------------------------------------------------
// prevElementStaff
//---------------------------------------------------------
Element* Measure::prevElementStaff(int staff)
{
Measure* prevM = prevMeasureMM();
@ -3229,108 +3236,116 @@ QString Measure::accessibleInfo() const
//-----------------------------------------------------------------------------
// stretchMeasure
// resize width of measure to stretch
// resize width of measure to targetWidth
//-----------------------------------------------------------------------------
void Measure::stretchMeasure(qreal stretch)
void Measure::stretchMeasure(qreal targetWidth)
{
bbox().setWidth(stretch);
bbox().setWidth(targetWidth);
int nstaves = score()->nstaves();
int minTick = 100000;
//---------------------------------------------------
// compute minTick and set ticks for all segments
//---------------------------------------------------
for (auto& s : _segments) {
int nticks;
if (s.isChordRestType()) {
const Segment* nseg = s.next(Segment::Type::ChordRest);
nticks = (nseg ? nseg->rtick() : ticks()) - s.rtick();
if (nticks) {
if (nticks < minTick)
minTick = nticks;
}
int minTick = ticks();
Segment* ns = first();
while (ns) {
Segment* s = ns;
ns = s->next();
int nticks = (ns ? ns->rtick() : ticks()) - s->rtick();
if (nticks) {
if (nticks < minTick)
minTick = nticks;
}
else
nticks = 0;
s.setTicks(nticks);
s->setTicks(nticks);
}
//---------------------------------------------------
// computeStretch
// compute stretch
//---------------------------------------------------
SpringMap springs;
qreal minimum = first()->pos().x();
// typedef std::multimap<qreal, Segment*, std::less<qreal>> SpringMap;
std::multimap<qreal, Segment*> springs;
for (auto& s : _segments) {
qreal str = 1.0;
qreal d;
Q_ASSERT(minTick > 0);
qreal minimumWidth = first()->pos().x();
for (Segment& s : _segments) {
int t = s.ticks();
if (t) {
if (minTick > 0)
// str += .6 * log(qreal(t) / qreal(minTick)) / log(2.0);
str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick));
d = s.width() / str;
qreal str = 1.0 + 0.865617 * log(qreal(t) / qreal(minTick)); // .6 * log(t / minTick) / log(2);
qreal d = s.width() / str;
s.setStretch(str);
springs.insert(std::pair<qreal, Segment*>(d, &s));
}
else {
str = 0.0; // dont stretch timeSig and key
d = 100000000.0; // CHECK
}
springs.insert(std::pair<qreal, Spring>(d, Spring(&s, str)));
minimum += s.width();
minimumWidth += s.width();
}
//---------------------------------------------------
// distribute stretch to segments
// compute 1/Force for a given Extend
//---------------------------------------------------
qreal force = sff(stretch, minimum, springs);
if (targetWidth > minimumWidth) {
qreal force = 0;
qreal c = 0.0;
for (auto i = springs.begin();;) {
c += i->second->stretch();
minimumWidth -= i->second->width();
qreal f = (targetWidth - minimumWidth) / c;
++i;
if (i == springs.end() || f <= i->first) {
force = f;
break;
}
}
//---------------------------------------------------
// distribute stretch to segments
//---------------------------------------------------
for (auto& i : springs) {
qreal stretch = force * i.second.stretch;
if (stretch < i.second.fix)
stretch = i.second.fix;
i.second.seg->setWidth(stretch);
}
for (auto& i : springs) {
qreal width = force * i.second->stretch();
if (width > i.second->width())
i.second->setWidth(width);
}
qreal x = first()->pos().x();
for (Segment* s = first(); s; s = s->next()) {
s->rxpos() = x;
x += s->width();
//---------------------------------------------------
// move segments to final position
//---------------------------------------------------
qreal x = first()->pos().x();
for (Segment& s : _segments) {
s.rxpos() = x;
x += s.width();
}
}
//---------------------------------------------------
// layout individual elements
//---------------------------------------------------
int tracks = nstaves * VOICES;
for (Segment* s = first(); s; s = s->next()) {
for (int track = 0; track < tracks; ++track) {
if (!score()->staff(track/VOICES)->show()) {
track += VOICES-1;
continue;
}
Element* e = s->element(track);
if (e == 0)
for (Segment& s : _segments) {
for (Element* e : s.elist()) {
if (!e)
continue;
Element::Type t = e->type();
Rest* rest = static_cast<Rest*>(e);
if (t == Element::Type::REPEAT_MEASURE || (t == Element::Type::REST && (isMMRest() || rest->isFullMeasureRest()))) {
int staffIdx = e->staffIdx();
if (t == Element::Type::REPEAT_MEASURE || (t == Element::Type::REST && (isMMRest() || toRest(e)->isFullMeasureRest()))) {
//
// element has to be centered in free space
// x1 - left measure position of free space
// x2 - right measure position of free space
Segment* s1 = s->prev() ? s->prev() : 0;
Segment* s1 = s.prev() ? s.prev() : 0;
Segment* s2;
for (s2 = s->next(); s2; s2 = s2->next()) {
for (s2 = s.next(); s2; s2 = s2->next()) {
if (!s2->isChordRestType())
break;
}
qreal x1 = s1 ? s1->x() + s1->minRight() : 0;
qreal x2 = s2 ? s2->x() - s2->minLeft() : width();
qreal x2 = s2 ? s2->x() - s2->minLeft() : targetWidth;
if (isMMRest()) {
Rest* rest = toRest(e);
//
// center multi measure rest
//
@ -3338,19 +3353,19 @@ void Measure::stretchMeasure(qreal stretch)
qreal w = x2 - x1 - 2 * d;
rest->setMMWidth(w);
StaffLines* sl = _mstaves[track/VOICES]->lines;
qreal x = x1 - s->x() + d;
StaffLines* sl = _mstaves[staffIdx]->lines;
qreal x = x1 - s.x() + d;
e->setPos(x, sl->staffHeight() * .5); // center vertically in measure
rest->layout();
s->createShape(track/VOICES);
s.createShape(staffIdx);
}
else { // if (rest->isFullMeasureRest()) {
//
// center full measure rest
//
rest->rxpos() = (x2 - x1 - e->width()) * .5 + x1 - s->x() - e->bbox().x();
rest->adjustReadPos();
s->createShape(track/VOICES); // DEBUG
e->rxpos() = (x2 - x1 - e->width()) * .5 + x1 - s.x() - e->bbox().x();
e->adjustReadPos();
s.createShape(staffIdx); // DEBUG
}
}
else if (t == Element::Type::REST)

View file

@ -263,8 +263,8 @@ class CmdState {
public:
LayoutFlags layoutFlags;
bool _excerptsChanged;
bool _instrumentsChanged;
bool _excerptsChanged { false };
bool _instrumentsChanged { false };
void reset();
UpdateMode updateMode() const { return _updateMode; }

View file

@ -767,10 +767,12 @@ void Segment::checkEmpty() const
// tick
//---------------------------------------------------------
#if 0
int Segment::tick() const
{
return _tick + measure()->tick();
}
#endif
//---------------------------------------------------------
// setTick
@ -1332,7 +1334,7 @@ void Segment::createShape(int staffIdx)
s.add(e->shape());
}
for (Element* e : _annotations) {
if (e->staffIdx() == staffIdx && e->visible())
if (e->staffIdx() == staffIdx && e->visible() && !e->isTempoText())
s.add(e->shape());
}
}
@ -1359,18 +1361,22 @@ qreal Segment::minRight() const
qreal Segment::minLeft(const Shape& sl) const
{
qreal distance = 0.0;
for (const Shape& sh : shapes())
distance = qMax(distance, sl.minHorizontalDistance(sh));
for (const Shape& sh : shapes()) {
qreal d = sl.minHorizontalDistance(sh);
if (d > distance)
distance = d;
}
return distance;
}
qreal Segment::minLeft() const
{
qreal distance = 0.0;
for (const Shape& sh : shapes())
distance = qMax(distance, sh.left());
for (const Shape& sh : shapes()) {
qreal l = sh.left();
if (l > distance)
distance = l;
}
return distance;
}
@ -1410,8 +1416,8 @@ qreal Segment::minHorizontalDistance(Segment* ns, bool systemHeaderGap) const
}
else
d = score()->styleP(StyleIdx::barNoteDistance);
qreal dd = minRight() + ns->minLeft();
w = qMax(d, dd + spatium());
qreal dd = minRight() + ns->minLeft() + spatium();
w = qMax(d, dd);
// d -= ns->minLeft() * .7; // hack
// d = qMax(d, ns->minLeft());
// d = qMax(d, spatium()); // minimum distance is one spatium

View file

@ -66,40 +66,42 @@ class Segment : public Element {
Q_PROPERTY(int tick READ tick)
Q_ENUMS(Type)
public:
enum class Type {
Invalid = 0x0,
BeginBarLine = 0x1,
Clef = 0x2, // type from Clef to TimeSig
KeySig = 0x4, // need to be in the order in which they
Ambitus = 0x8, // appear in a measure
TimeSig = 0x10,
StartRepeatBarLine = 0x20,
BarLine = 0x40,
Breath = 0x80,
ChordRest = 0x100,
EndBarLine = 0x200,
KeySigAnnounce = 0x400,
TimeSigAnnounce = 0x800,
All = -1
};
public:
// Type need to be in the order in which they appear in a measure
enum class Type {
Invalid = 0x0,
BeginBarLine = 0x1,
Clef = 0x2,
KeySig = 0x4,
Ambitus = 0x8,
TimeSig = 0x10,
StartRepeatBarLine = 0x20,
BarLine = 0x40,
Breath = 0x80,
ChordRest = 0x100,
EndBarLine = 0x200,
KeySigAnnounce = 0x400,
TimeSigAnnounce = 0x800,
All = -1
};
private:
Segment* _next; // linked list of segments inside a measure
Segment* _prev;
mutable bool _empty; // cached value
mutable bool _written { false }; // used for write()
std::vector<Element*> _annotations;
std::vector<Element*> _elist; // Element storage, size = staves * VOICES.
std::vector<Shape> _shapes; // size = staves
std::vector<qreal> _dotPosX; // size = staves
Type _segmentType { Type::Invalid };
Spatium _extraLeadingSpace;
qreal _stretch;
int _tick;
int _ticks;
Spatium _extraLeadingSpace;
Type _segmentType { Type::Invalid };
std::vector<Element*> _annotations;
std::vector<Element*> _elist; ///< Element storage, size = staves * VOICES.
std::vector<Shape> _shapes; // size = staves
std::vector<qreal> _dotPosX; ///< size = staves
mutable bool _empty; // cached value
mutable bool _written { false }; // used for write()
void init();
void checkEmpty() const;
@ -176,8 +178,11 @@ public:
void removeGeneratedElements();
bool empty() const { return _empty; }
void fixStaffIdx();
qreal stretch() const { return _stretch; }
void setStretch(qreal v) { _stretch = v; }
void setTick(int);
virtual int tick() const override;
virtual int tick() const override { return _tick + parent()->tick(); }
virtual int rtick() const override { return _tick; } // tickposition relative to measure start
void setRtick(int val) { _tick = val; }
int ticks() const { return _ticks; }

View file

@ -83,6 +83,10 @@ void SegmentList::check()
if (l != _last) {
qFatal("SegmentList::check: bad last");
}
if (f->prev())
qFatal("SegmentList::check: first has prev");
if (l->next())
qFatal("SegmentList::check: last has next");
if (n != _size) {
qFatal("SegmentList::check: counted %d but _size is %d", n, _size);
_size = n;
@ -107,34 +111,46 @@ void SegmentList::insert(Segment* e, Segment* el)
e->setPrev(el->prev());
el->prev()->setNext(e);
el->setPrev(e);
check();
}
check();
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void SegmentList::remove(Segment* el)
void SegmentList::remove(Segment* e)
{
#ifndef NDEBUG
check();
bool found = false;
for (Segment* s = _first; s; s = s->next()) {
if (e == s) {
found = true;
break;
}
}
if (!found) {
qFatal("segment %p %s not in list", e, e->subTypeName());
}
#endif
--_size;
if (el == _first) {
if (e == _first) {
_first = _first->next();
if (_first)
_first->setPrev(0);
if (el == _last)
if (e == _last)
_last = 0;
}
else if (el == _last) {
else if (e == _last) {
_last = _last->prev();
if (_last)
_last->setNext(0);
}
else {
el->prev()->setNext(el->next());
el->next()->setPrev(el->prev());
e->prev()->setNext(e->next());
e->next()->setPrev(e->prev());
}
check();
}
//---------------------------------------------------------
@ -171,54 +187,6 @@ void SegmentList::push_front(Segment* e)
check();
}
//---------------------------------------------------------
// insert
//---------------------------------------------------------
void SegmentList::insert(Segment* seg)
{
#ifndef NDEBUG
// qDebug("insertSeg <%s> %p %p %p", seg->subTypeName(), seg->prev(), seg, seg->next());
check();
for (Segment* s = _first; s; s = s->next()) {
if (s == seg) {
qFatal("SegmentList::insert: already in list");
}
}
if (seg->prev()) {
Segment* s;
for (s = _first; s; s = s->next()) {
if (s == seg->prev())
break;
}
if (s != seg->prev()) {
qFatal("SegmentList::insert: seg->prev() not in list");
}
}
if (seg->next()) {
Segment* s;
for (s = _first; s; s = s->next()) {
if (s == seg->next())
break;
}
if (s != seg->next()) {
qFatal("SegmentList::insert: seg->next() not in list");
}
}
#endif
if (seg->prev())
seg->prev()->setNext(seg);
else
_first = seg;
if (seg->next())
seg->next()->setPrev(seg);
else
_last = seg;
++_size;
check();
}
//---------------------------------------------------------
// firstCRSegment
//---------------------------------------------------------

View file

@ -45,8 +45,7 @@ class SegmentList {
void remove(Segment*);
void push_back(Segment*);
void push_front(Segment*);
void insert(Segment*);
void insert(Segment* e, Segment* el);
void insert(Segment* e, Segment* el); // insert e before el
class iterator {
Segment* p;

View file

@ -15,7 +15,7 @@
namespace Ms {
// #define DEBUG_SHAPES
#define DEBUG_SHAPES
class Segment;

View file

@ -279,31 +279,29 @@ QVariant TempoText::propertyDefault(P_ID id) const
//---------------------------------------------------------
// layout
// called after Measure->stretchMeasure()
//---------------------------------------------------------
void TempoText::layout()
{
setPos(textStyle().offset(spatium()));
Text::layout1();
// tempo text on first chordrest of measure should align over time sig if present
//
Segment* s = segment();
if (s && !s->rtick()) {
// tempo text on first chordrest of measure should align over time sig if present
Segment* p = segment()->prev(Segment::Type::TimeSig);
if (p) {
rxpos() -= s->x() - p->x();
Element* e = p->element(staffIdx() * VOICES);
if (e)
rxpos() += e->x();
// correct user offset in older scores
if (score()->mscVersion() <= 114 && !userOff().isNull())
rUserXoffset() += s->x() - p->x();
}
}
if (placement() == Element::Placement::BELOW) {
if (placement() == Element::Placement::BELOW)
rypos() = -rypos() + 4 * spatium();
// rUserYoffset() *= -1;
// text height ?
}
adjustReadPos();
}

View file

@ -194,7 +194,7 @@
<Segment>
<subtype>0</subtype>
<off2 x="0" y="0"/>
<pos x="33.8175" y="6.60645"/>
<pos x="34.0159" y="6.60645"/>
</Segment>
<beginText>
<text>cresc.</text>

View file

@ -194,7 +194,7 @@
<Segment>
<subtype>0</subtype>
<off2 x="0" y="0"/>
<pos x="33.8175" y="6.60645"/>
<pos x="34.0159" y="6.60645"/>
</Segment>
<beginText>
<text>cresc.</text>

View file

@ -337,7 +337,7 @@ MasterScore* TestParts::doAddBreath()
Measure* m = score->firstMeasure();
Segment* s = m->tick2segment(MScore::division);
Ms::Chord* chord = static_cast<Ms::Chord*>(s->element(0));
Ms::Chord* chord = toChord(s->element(0));
Note* note = chord->upNote();
DropData dd;
dd.view = 0;