[engraving] moved layout Tie

This commit is contained in:
Igor Korsukov 2023-05-05 17:07:48 +03:00
parent e49dca5c5d
commit 07629d88de
9 changed files with 280 additions and 272 deletions

View File

@ -55,6 +55,8 @@
#include "libmscore/undo.h"
#include "libmscore/utils.h"
#include "slurtielayout.h"
using namespace mu::engraving;
void ChordLayout::layout(Chord* item, LayoutContext& ctx)
@ -1975,7 +1977,7 @@ void ChordLayout::updateLineAttachPoints(Chord* chord, bool isFirstInMeasure)
for (Note* note : chord->notes()) {
Tie* tieBack = note->tieBack();
if (tieBack && tieBack->startNote()->findMeasure() != note->findMeasure()) {
tieBack->layoutBack(note->findMeasure()->system());
SlurTieLayout::tieLayoutBack(tieBack, note->findMeasure()->system());
}
}
}
@ -1984,7 +1986,7 @@ void ChordLayout::updateLineAttachPoints(Chord* chord, bool isFirstInMeasure)
if (tie) {
Note* endNote = tie->endNote();
if (endNote && endNote->findMeasure() == note->findMeasure()) {
tie->layoutFor(note->findMeasure()->system()); // line attach points are updated here
SlurTieLayout::tieLayoutFor(tie, note->findMeasure()->system()); // line attach points are updated here
}
}
}

View File

@ -53,6 +53,7 @@
#include "lyricslayout.h"
#include "layoutmeasure.h"
#include "layouttuplets.h"
#include "slurtielayout.h"
#include "log.h"
@ -1409,7 +1410,7 @@ void LayoutSystem::layoutTies(Chord* ch, System* system, const Fraction& stick)
for (Note* note : ch->notes()) {
Tie* t = note->tieFor();
if (t) {
TieSegment* ts = t->layoutFor(system);
TieSegment* ts = SlurTieLayout::tieLayoutFor(t, system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translate(ts->pos()));
}
@ -1417,7 +1418,7 @@ void LayoutSystem::layoutTies(Chord* ch, System* system, const Fraction& stick)
t = note->tieBack();
if (t) {
if (t->startNote()->tick() < stick) {
TieSegment* ts = t->layoutBack(system);
TieSegment* ts = SlurTieLayout::tieLayoutBack(t, system);
if (ts && ts->addToSkyline()) {
staff->skyline().add(ts->shape().translate(ts->pos()));
}

View File

@ -981,3 +981,259 @@ void SlurTieLayout::fixArticulations(Slur* item, PointF& pt, Chord* c, double up
}
}
}
//---------------------------------------------------------
// layoutFor
// layout the first SpannerSegment of a slur
//---------------------------------------------------------
TieSegment* SlurTieLayout::tieLayoutFor(Tie* item, System* system)
{
// do not layout ties in tablature if not showing back-tied fret marks
StaffType* st = item->staff()->staffType(item->startNote() ? item->startNote()->tick() : Fraction(0, 1));
if (st && st->isTabStaff() && !st->showBackTied()) {
if (!item->segmentsEmpty()) {
item->eraseSpannerSegments();
}
return nullptr;
}
//
// show short bow
//
if (item->startNote() == 0 || item->endNote() == 0) {
if (item->startNote() == 0) {
LOGD("no start note");
return 0;
}
Chord* c1 = item->startNote()->chord();
item->setTick(c1->tick());
if (item->_slurDirection == DirectionV::AUTO) {
bool simpleException = st && st->isSimpleTabStaff();
if (st && st->isSimpleTabStaff()) {
item->_up = isUpVoice(c1->voice());
} else {
if (c1->measure()->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks())) {
// in polyphonic passage, ties go on the stem side
item->_up = simpleException ? isUpVoice(c1->voice()) : c1->up();
} else {
item->_up = !c1->up();
}
}
} else {
item->_up = item->_slurDirection == DirectionV::UP ? true : false;
}
item->fixupSegments(1);
TieSegment* segment = item->segmentAt(0);
segment->setSpannerSegmentType(SpannerSegmentType::SINGLE);
segment->setSystem(item->startNote()->chord()->segment()->measure()->system());
SlurPos sPos;
tiePos(item, &sPos);
segment->adjustY(sPos.p1, sPos.p2);
segment->finalizeSegment();
return segment;
}
item->calculateDirection();
SlurPos sPos;
tiePos(item, &sPos); // get unadjusted x values and determine inside or outside
item->setPos(0, 0);
int n;
if (sPos.system1 != sPos.system2) {
n = 2;
sPos.p2 = PointF(system->lastNoteRestSegmentX(true), sPos.p1.y());
} else {
n = 1;
}
item->fixupSegments(n);
TieSegment* segment = item->segmentAt(0);
segment->setSystem(system); // Needed to populate System.spannerSegments
Chord* c1 = item->startNote()->chord();
item->setTick(c1->tick());
segment->adjustY(sPos.p1, sPos.p2); // adjust vertically
segment->setSpannerSegmentType(sPos.system1 != sPos.system2 ? SpannerSegmentType::BEGIN : SpannerSegmentType::SINGLE);
segment->adjustX(); // adjust horizontally for inside-style ties
segment->finalizeSegment(); // compute bezier and set bbox
segment->addLineAttachPoints(); // add attach points to start and end note
return segment;
}
//---------------------------------------------------------
// layoutBack
// layout the second SpannerSegment of a split slur
//---------------------------------------------------------
TieSegment* SlurTieLayout::tieLayoutBack(Tie* item, System* system)
{
// do not layout ties in tablature if not showing back-tied fret marks
StaffType* st = item->staff()->staffType(item->startNote() ? item->startNote()->tick() : Fraction(0, 1));
if (st->isTabStaff() && !st->showBackTied()) {
if (!item->segmentsEmpty()) {
item->eraseSpannerSegments();
}
return nullptr;
}
SlurPos sPos;
tiePos(item, &sPos);
item->fixupSegments(2);
TieSegment* segment = item->segmentAt(1);
segment->setSystem(system);
double x = system ? system->firstNoteRestSegmentX(true) : 0;
segment->adjustY(PointF(x, sPos.p2.y()), sPos.p2);
segment->setSpannerSegmentType(SpannerSegmentType::END);
segment->adjustX();
segment->finalizeSegment();
segment->addLineAttachPoints();
return segment;
}
//---------------------------------------------------------
// slurPos
// Calculate position of start- and endpoint of slur
// relative to System() position.
//---------------------------------------------------------
void SlurTieLayout::tiePos(Tie* item, SlurPos* sp)
{
const StaffType* staffType = item->staffType();
bool useTablature = staffType->isTabStaff();
double _spatium = item->spatium();
double hw = item->startNote()->tabHeadWidth(staffType); // if staffType == 0, defaults to headWidth()
/* Inside-style and Outside-style ties
Outside ties connect above the notehead, in the middle. Ideally, we'd use opticalcenter for this, but
that Smufl anchor is not available for noteheads yet. For this reason, we rely on Note::outsideTieAttachX()
which makes its best guess as to where ties should connect.
As for y connection point, inside-style ties are decided by the space or line the note occupies in TieSegment::adjustY()
so we don't need to worry about that. Outside-style ties will be 0.125 spatium from the top or bottom of the notehead.
We can parameterize that later, but this describes only a minimum distance from the notehead, it can be changed, again,
in TieSegment::adjustY().
*/
Chord* sc = item->startNote()->chord();
Chord* ec = item->endNote() ? item->endNote()->chord() : nullptr;
sp->system1 = sc->measure()->system();
if (!sp->system1) {
Measure* m = sc->measure();
LOGD("No system: measure is %d has %d count %d", m->isMMRest(), m->hasMMRest(), m->mmRestCount());
}
double x1, y1;
double x2, y2;
// determine attachment points
// similar code is used in Chord::layoutPitched()
// to allocate extra space to enforce minTieLength
// so keep these in sync
if (sc->notes().size() > 1 || (ec && ec->notes().size() > 1)) {
item->_isInside = true;
} else {
item->_isInside = false;
}
sp->p1 = sc->pos() + sc->segment()->pos() + sc->measure()->pos();
//------p1
y1 = item->startNote()->pos().y();
y2 = item->endNote() ? item->endNote()->pos().y() : y1;
// force tie to be horizontal except for cross-staff or if there is a difference of line (tpc, clef)
int line1 = useTablature ? item->startNote()->string() : item->startNote()->line();
int line2 = line1;
if (item->endNote()) {
line2 = useTablature ? item->endNote()->string() : item->endNote()->line();
}
bool isHorizontal = ec ? line1 == line2 && sc->vStaffIdx() == ec->vStaffIdx() : true;
y1 += item->startNote()->bbox().y();
if (item->endNote()) {
y2 += item->endNote()->bbox().y();
}
if (!item->up()) {
y1 += item->startNote()->bbox().height();
if (item->endNote()) {
y2 += item->endNote()->bbox().height();
}
}
if (!item->endNote()) {
y2 = y1;
}
// ensure that horizontal ties remain horizontal
if (isHorizontal) {
y1 = item->_up ? std::min(y1, y2) : std::max(y1, y2);
y2 = item->_up ? std::min(y1, y2) : std::max(y1, y2);
}
if (item->_isInside) {
x1 = item->startNote()->pos().x() + hw; // the offset for these will be decided in TieSegment::adjustX()
} else {
if (sc->stem() && sc->stem()->visible() && sc->up() && item->_up) {
// usually, outside ties start in the middle of the notehead, but
// for up-ties on up-stems, we'll start at the end of the notehead
// to avoid the stem
x1 = item->startNote()->pos().x() + hw;
} else {
x1 = item->startNote()->outsideTieAttachX(item->_up);
}
}
sp->p1 += PointF(x1, y1);
//------p2
if (!ec) {
sp->p2 = sp->p1 + PointF(_spatium * 3, 0.0);
sp->system2 = sp->system1;
return;
}
sp->p2 = ec->pos() + ec->segment()->pos() + ec->measure()->pos();
sp->system2 = ec->measure()->system();
if (item->isInside()) {
x2 = item->endNote()->x();
} else {
if (ec->stem() && ec->stem()->visible() && !ec->up() && !item->_up) {
// as before, xo should account for stems that could get in the way
x2 = item->endNote()->x();
} else {
x2 = item->endNote()->outsideTieAttachX(item->_up);
}
}
sp->p2 += PointF(x2, y2);
// adjust for cross-staff
if (sc->vStaffIdx() != item->staffIdx() && sp->system1) {
double diff = sp->system1->staff(sc->vStaffIdx())->y() - sp->system1->staff(item->staffIdx())->y();
sp->p1.ry() += diff;
}
if (ec->vStaffIdx() != item->staffIdx() && sp->system2) {
double diff = sp->system2->staff(ec->vStaffIdx())->y() - sp->system2->staff(item->staffIdx())->y();
sp->p2.ry() += diff;
}
/// adjusting ties for notes in circles
if (Tie::engravingConfiguration()->enableExperimentalFretCircle() && item->staff()->staffType()->isCommonTabStaff()) {
auto adjustTie = [item](Chord* ch, PointF& coord, bool tieStart) {
const Fraction halfFraction = Fraction(1, 2);
if (ch && ch->ticks() >= halfFraction) {
for (EngravingItem* e : ch->el()) {
if (e && e->isFretCircle()) {
FretCircle* fretCircle = toFretCircle(e);
coord += PointF(0, fretCircle->offsetFromUpNote() * (item->up() ? -1 : 1));
if (item->isInside()) {
coord += PointF(fretCircle->sideOffset() * (tieStart ? 1 : -1), 0);
}
break;
}
}
}
};
adjustTie(sc, sp->p1, true);
adjustTie(ec, sp->p2, false);
}
}

View File

@ -30,6 +30,8 @@ struct SlurPos;
class SpannerSegment;
class System;
class Chord;
class TieSegment;
class Tie;
class SlurTieLayout
{
@ -37,9 +39,14 @@ public:
static void layout(Slur* item, LayoutContext& ctx);
static SpannerSegment* layoutSystem(Slur* item, System* system);
static TieSegment* tieLayoutFor(Tie* item, System* system);
static TieSegment* tieLayoutBack(Tie* item, System* system);
private:
static void slurPos(Slur* item, SlurPos* sp);
static void fixArticulations(Slur* item, PointF& pt, Chord* c, double up, bool stemSide);
static void tiePos(Tie* item, SlurPos* sp);
};
}

View File

@ -30,6 +30,7 @@
#include "style/style.h"
#include "layout/tlayout.h"
#include "layout/slurtielayout.h"
#include "accidental.h"
#include "arpeggio.h"
@ -2043,12 +2044,12 @@ void Chord::layoutSpanners(System* system, const Fraction& stick)
for (const Note* note : notes()) {
Tie* t = note->tieFor();
if (t) {
t->layoutFor(system);
SlurTieLayout::tieLayoutFor(t, system);
}
t = note->tieBack();
if (t) {
if (t->startNote()->tick() < stick) {
t->layoutBack(system);
SlurTieLayout::tieLayoutBack(t, system);
}
}
for (Spanner* sp : note->spannerBack()) {

View File

@ -95,7 +95,7 @@ public:
void layout() override;
SpannerSegment* layoutSystem(System*) override;
void setTrack(track_idx_t val) override;
void slurPos(SlurPos*) override {}
void computeUp();
void setSourceStemArrangement(int v) { _sourceStemArrangement = v; }

View File

@ -174,7 +174,6 @@ public:
SlurStyleType styleType() const { return _styleType; }
void setStyleType(SlurStyleType type) { _styleType = type; }
virtual void slurPos(SlurPos*) = 0;
virtual SlurTieSegment* newSlurTieSegment(System* parent) = 0;
PropertyValue getProperty(Pid propertyId) const override;

View File

@ -851,151 +851,6 @@ void TieSegment::addLineAttachPoints()
}
}
//---------------------------------------------------------
// slurPos
// Calculate position of start- and endpoint of slur
// relative to System() position.
//---------------------------------------------------------
void Tie::slurPos(SlurPos* sp)
{
const StaffType* staffType = this->staffType();
bool useTablature = staffType->isTabStaff();
double _spatium = spatium();
double hw = startNote()->tabHeadWidth(staffType); // if staffType == 0, defaults to headWidth()
/* Inside-style and Outside-style ties
Outside ties connect above the notehead, in the middle. Ideally, we'd use opticalcenter for this, but
that Smufl anchor is not available for noteheads yet. For this reason, we rely on Note::outsideTieAttachX()
which makes its best guess as to where ties should connect.
As for y connection point, inside-style ties are decided by the space or line the note occupies in TieSegment::adjustY()
so we don't need to worry about that. Outside-style ties will be 0.125 spatium from the top or bottom of the notehead.
We can parameterize that later, but this describes only a minimum distance from the notehead, it can be changed, again,
in TieSegment::adjustY().
*/
Chord* sc = startNote()->chord();
Chord* ec = endNote() ? endNote()->chord() : nullptr;
sp->system1 = sc->measure()->system();
if (!sp->system1) {
Measure* m = sc->measure();
LOGD("No system: measure is %d has %d count %d", m->isMMRest(), m->hasMMRest(), m->mmRestCount());
}
double x1, y1;
double x2, y2;
// determine attachment points
// similar code is used in Chord::layoutPitched()
// to allocate extra space to enforce minTieLength
// so keep these in sync
if (sc->notes().size() > 1 || (ec && ec->notes().size() > 1)) {
_isInside = true;
} else {
_isInside = false;
}
sp->p1 = sc->pos() + sc->segment()->pos() + sc->measure()->pos();
//------p1
y1 = startNote()->pos().y();
y2 = endNote() ? endNote()->pos().y() : y1;
// force tie to be horizontal except for cross-staff or if there is a difference of line (tpc, clef)
int line1 = useTablature ? startNote()->string() : startNote()->line();
int line2 = line1;
if (endNote()) {
line2 = useTablature ? endNote()->string() : endNote()->line();
}
bool isHorizontal = ec ? line1 == line2 && sc->vStaffIdx() == ec->vStaffIdx() : true;
y1 += startNote()->bbox().y();
if (endNote()) {
y2 += endNote()->bbox().y();
}
if (!up()) {
y1 += startNote()->bbox().height();
if (endNote()) {
y2 += endNote()->bbox().height();
}
}
if (!endNote()) {
y2 = y1;
}
// ensure that horizontal ties remain horizontal
if (isHorizontal) {
y1 = _up ? std::min(y1, y2) : std::max(y1, y2);
y2 = _up ? std::min(y1, y2) : std::max(y1, y2);
}
if (_isInside) {
x1 = startNote()->pos().x() + hw; // the offset for these will be decided in TieSegment::adjustX()
} else {
if (sc->stem() && sc->stem()->visible() && sc->up() && _up) {
// usually, outside ties start in the middle of the notehead, but
// for up-ties on up-stems, we'll start at the end of the notehead
// to avoid the stem
x1 = startNote()->pos().x() + hw;
} else {
x1 = startNote()->outsideTieAttachX(_up);
}
}
sp->p1 += PointF(x1, y1);
//------p2
if (!ec) {
sp->p2 = sp->p1 + PointF(_spatium * 3, 0.0);
sp->system2 = sp->system1;
return;
}
sp->p2 = ec->pos() + ec->segment()->pos() + ec->measure()->pos();
sp->system2 = ec->measure()->system();
if (isInside()) {
x2 = endNote()->x();
} else {
if (ec->stem() && ec->stem()->visible() && !ec->up() && !_up) {
// as before, xo should account for stems that could get in the way
x2 = endNote()->x();
} else {
x2 = endNote()->outsideTieAttachX(_up);
}
}
sp->p2 += PointF(x2, y2);
// adjust for cross-staff
if (sc->vStaffIdx() != staffIdx() && sp->system1) {
double diff = sp->system1->staff(sc->vStaffIdx())->y() - sp->system1->staff(staffIdx())->y();
sp->p1.ry() += diff;
}
if (ec->vStaffIdx() != staffIdx() && sp->system2) {
double diff = sp->system2->staff(ec->vStaffIdx())->y() - sp->system2->staff(staffIdx())->y();
sp->p2.ry() += diff;
}
/// adjusting ties for notes in circles
if (engravingConfiguration()->enableExperimentalFretCircle() && staff()->staffType()->isCommonTabStaff()) {
auto adjustTie = [this](Chord* ch, PointF& coord, bool tieStart) {
const Fraction halfFraction = Fraction(1, 2);
if (ch && ch->ticks() >= halfFraction) {
for (EngravingItem* item : ch->el()) {
if (item && item->isFretCircle()) {
FretCircle* fretCircle = toFretCircle(item);
coord += PointF(0, fretCircle->offsetFromUpNote() * (up() ? -1 : 1));
if (isInside()) {
coord += PointF(fretCircle->sideOffset() * (tieStart ? 1 : -1), 0);
}
break;
}
}
}
};
adjustTie(sc, sp->p1, true);
adjustTie(ec, sp->p2, false);
}
}
//---------------------------------------------------------
// Tie
//---------------------------------------------------------
@ -1133,117 +988,6 @@ void Tie::calculateDirection()
}
}
//---------------------------------------------------------
// layoutFor
// layout the first SpannerSegment of a slur
//---------------------------------------------------------
TieSegment* Tie::layoutFor(System* system)
{
// do not layout ties in tablature if not showing back-tied fret marks
StaffType* st = staff()->staffType(startNote() ? startNote()->tick() : Fraction(0, 1));
if (st && st->isTabStaff() && !st->showBackTied()) {
if (!segmentsEmpty()) {
eraseSpannerSegments();
}
return nullptr;
}
//
// show short bow
//
if (startNote() == 0 || endNote() == 0) {
if (startNote() == 0) {
LOGD("no start note");
return 0;
}
Chord* c1 = startNote()->chord();
setTick(c1->tick());
if (_slurDirection == DirectionV::AUTO) {
bool simpleException = st && st->isSimpleTabStaff();
if (st && st->isSimpleTabStaff()) {
_up = isUpVoice(c1->voice());
} else {
if (c1->measure()->hasVoices(c1->staffIdx(), c1->tick(), c1->actualTicks())) {
// in polyphonic passage, ties go on the stem side
_up = simpleException ? isUpVoice(c1->voice()) : c1->up();
} else {
_up = !c1->up();
}
}
} else {
_up = _slurDirection == DirectionV::UP ? true : false;
}
fixupSegments(1);
TieSegment* segment = segmentAt(0);
segment->setSpannerSegmentType(SpannerSegmentType::SINGLE);
segment->setSystem(startNote()->chord()->segment()->measure()->system());
SlurPos sPos;
slurPos(&sPos);
segment->adjustY(sPos.p1, sPos.p2);
segment->finalizeSegment();
return segment;
}
calculateDirection();
SlurPos sPos;
slurPos(&sPos); // get unadjusted x values and determine inside or outside
setPos(0, 0);
int n;
if (sPos.system1 != sPos.system2) {
n = 2;
sPos.p2 = PointF(system->lastNoteRestSegmentX(true), sPos.p1.y());
} else {
n = 1;
}
fixupSegments(n);
TieSegment* segment = segmentAt(0);
segment->setSystem(system); // Needed to populate System.spannerSegments
Chord* c1 = startNote()->chord();
setTick(c1->tick());
segment->adjustY(sPos.p1, sPos.p2); // adjust vertically
segment->setSpannerSegmentType(sPos.system1 != sPos.system2 ? SpannerSegmentType::BEGIN : SpannerSegmentType::SINGLE);
segment->adjustX(); // adjust horizontally for inside-style ties
segment->finalizeSegment(); // compute bezier and set bbox
segment->addLineAttachPoints(); // add attach points to start and end note
return segment;
}
//---------------------------------------------------------
// layoutBack
// layout the second SpannerSegment of a split slur
//---------------------------------------------------------
TieSegment* Tie::layoutBack(System* system)
{
// do not layout ties in tablature if not showing back-tied fret marks
StaffType* st = staff()->staffType(startNote() ? startNote()->tick() : Fraction(0, 1));
if (st->isTabStaff() && !st->showBackTied()) {
if (!segmentsEmpty()) {
eraseSpannerSegments();
}
return nullptr;
}
SlurPos sPos;
slurPos(&sPos);
fixupSegments(2);
TieSegment* segment = segmentAt(1);
segment->setSystem(system);
double x = system ? system->firstNoteRestSegmentX(true) : 0;
segment->adjustY(PointF(x, sPos.p2.y()), sPos.p2);
segment->setSpannerSegmentType(SpannerSegmentType::END);
segment->adjustX();
segment->finalizeSegment();
segment->addLineAttachPoints();
return segment;
}
//---------------------------------------------------------
// setStartNote
//---------------------------------------------------------

View File

@ -83,9 +83,6 @@ class Tie final : public SlurTie
static Note* editStartNote;
static Note* editEndNote;
private:
bool _isInside{ false };
public:
Tie(EngravingItem* parent = 0);
@ -102,11 +99,6 @@ public:
void calculateDirection();
void slurPos(SlurPos*) override;
TieSegment* layoutFor(System*);
TieSegment* layoutBack(System*);
TieSegment* frontSegment() { return toTieSegment(Spanner::frontSegment()); }
const TieSegment* frontSegment() const { return toTieSegment(Spanner::frontSegment()); }
TieSegment* backSegment() { return toTieSegment(Spanner::backSegment()); }
@ -115,6 +107,12 @@ public:
const TieSegment* segmentAt(int n) const { return toTieSegment(Spanner::segmentAt(n)); }
SlurTieSegment* newSlurTieSegment(System* parent) override { return new TieSegment(parent); }
private:
friend class SlurTieLayout;
bool _isInside = false;
};
} // namespace mu::engraving
#endif